内部培训讲义精简
理解数据关系
- Entity: 对象 / 数据对象 / 点
- Relation: 关系 / 边
- 至多一 / 一对一 / 一对多
- 对象是抽象的, 关系是具象的, E通过R体现
区分关系和事实, 认识到关系的时效性
- 例:
- 事实 (人-地点-商品): 小明在大学城喝奶茶
- 关系: 男性-番禺-饮料
- 不要错把关系当事实: 小明做了变性手术, 大学城划归天河区
- 事实推导出关系 / 关系变更等于事实
- 关系一定是有时效限定
- 事实: 日期-男性-番禺-饮料
- 关系=Table
- 事实=Log
相关名词
- 属性: 至多一 / 一对一关系
- 数值属性, 虽然没有关联实体, 单也可以认为是对象概念, 如年龄, 价格; 并衍生如老年人, 低价角度的分析需求
- 关系表 vs 属性表: 其实没有明确区别
- 既然属性是关系, 就不能假设属性是不变的, 比如说姓名, 性别. 属性变化的来源: 需求变更, 数据订正
- 标签 (多属性): 一对多关系
- 指标: 关系的统计值
- 知识图谱 / 数据链路 / 数据字典 / 数据地图 / …: 数据关系图
为什么要做数据同步/关系冗余 ?
- 数据同步: 面向不同业务问题的数据物化视图
- 解决查询性能问题: 关系涉及数据量较大, 耗时
- 例: 年龄-体重的关系, 需要遍历所有的人群
- 对外一致的关系呈现, 屏蔽具体业务侧呈现关系的规则复杂性, 例:
- “广告-行业”关系的呈现优先级规则会不断演化, 业务方不需要关系具体规则变更: (新增) 广告-应用-行业 > (新增) 广告-品牌-行业 > 广告-文本识别-行业
数据同步SLA
- 从业务视角来看
- 一致性: 不同来源推导的相同数据关系要对的上
- 实时性: 数据尽快可见, 数据价值是有时效性的
- 从技术视角来看
- 减少推导最终关系结果所需要查询的数据量
- 提高数据更新的有效率
为什么要做增量同步?
- 提高数据时效性
- 减少不必要的计算/传输
- 例子: 统计各省人数
- 全量同步: 遍历所有人口统计
- 增量同步:
- 监听各省出生/死亡人口事件
- 此外需要额外监听户籍变更事件
- 一些有数据淘汰的场景下, 全量同步已经不可行
- 历史累计销量: 每期做快照表, 下期快照=上期快照+当期订单
增量同步的事件驱动来源
- 基于数据行modify_time筛选
- 定期任务方式执行
- 数据库触发器 (禁止使用, 避免数据库负担)
- CDC / binlog
- 实时消费订阅
- 可以知道具体发生变更字段, 以及变更前后信息, 从而帮助细化需同步数据范围
- 例子: 户籍变更事件, 转出地人口-1, 转入地人口+1即可
增量同步的流程复杂性
关系变更分类:
- 新增
- 解除
- 更新 = 解除+新增
例子1: “筛选指定品类投放用素材”需求: 短路”素材-创意-广告-商品-品类”链路:
从监听商品识别品类关系变更角度来看:
- 商品新增品类识别: a. 顺流而上 顺着 商品-广告-创意-素材 链路找到关联的素材列表 b. 做素材-品类关系的增量更新
- 商品修改/删除品类识别: a. 同1-a步骤, 找到受到影响的素材列表 b. (逆流而下) 沿路返回: 素材-创意-广告-商品 找到这批素材所关联的所有商品的品类 c. 做素材-品类关系的覆盖更新
其他关系链路上也需要监听, 原因:
- 主体本身识别错误, 合并事件: 素材合并 / 创意合并 / 广告合并 / 商品合并
- 广告商品关系变更, 旧商品关联新广告
- 广告商品关系建立晚于商品品类识别
- … 等等
例子2: “文章(推广商品识别)品牌”筛选需求: 短路”文章-<带货>-商品, 商品-<识别>-品牌"链路:识别>带货>
- ETL任务1 (文章-商品): 文章提及商品链接提取, 抓取相关商品信息入库
- ETL任务2 (商品-品牌):
- 商品触发算法识别品牌
- 品牌识别策略迭代, 需定向重跑相关商品
- 数据同步任务:
- 监听-商品-品牌-新增关系: 查询商品关联文章->增量更新
- 监听-文章-商品-新增关系: 查询商品关联品牌->增量更新
- 监听-商品-品牌-删除变更关系: 查询商品关联文章->反查文章关联所有商品->查询商品关联所有品牌->覆盖更新
- 监听-文章-商品-删除变更关系: 查询文章关联商品->查询商品关联品牌->覆盖更新
增量同步套路总结
- 新增关系: 顺流而上-增量写
- 更新/解除关系: 顺流而上-逆流而下-覆盖写
同步数据不一致/复杂性来源
- 没有处理变更/解除关系, 逆流而下覆盖更新
- 关系当事实, 没有监听变更
- 关系建立先后顺序做了错误假定
以上对于基础数据关系的期望:
- 发展健康稳定的关系: 只做关系新增, 不做关系变更/删除
- 保障关系链路建立的严格顺序性
很难!!!原因:
- 采集事实数据错误需要订正
- 不能第一时间区分唯一主体, 需要做后置的主体合并策略 (是不是同一个广告???)
- ETL阶段任务异步化策略
- 算法识别/策略规则的迭代
- 异构数据源本身同步延迟导致的不一致
- …
增量同步性能问题分析
一对多关系的扇入/扇出导致关系查询路径遍历数据量膨胀, 需要写入行数放大.
例: 3个主体2个关系, 单关系扇出/扇入上限10, 1行事件”顺流而上”影响100行, 再”逆流而下”影响10000行数据.
应对策略:
- 基础数据关系维护明确并限制一对多关系上限
- 具体业务价值判断, 识别”渣男”关系, 有效止损
- 1个文章提及了1000+商品: SEO文章, 排除
- 1个商品被1000+文章识别了: 热推品牌, 热卖商品
- 关系链路遍历时及时止损, 牺牲精确性换性能
- 走扇出最小路径遍历同步数据, 高扇出路径从统计上认为单事件变更会导致最终结果变化概率极低, 或者不显著
增量同步任务方针
- 正确识别结果数据上游来源
- 避免关系当作事实, 确保没有遗漏变更事件
- 避免对关系建立的先后顺序做假设
- 新增关系增量同步, 关系变更/解除要做全量同步
- 致力于最短数据链路同步维护: 避免同时做关系维护和关系短路操作
- 职责单一, “串联”优先于”并联”, 避免同时写入多个下游
- 数据写入保持幂等性
- 禁止
insert ... on duplicate key update cnt=values(cnt)+cnt
, 很难保证统计正确性 - 利用下游数据存储特性做集合结构去重来实现增量同步是允许的
- 禁止
代码逻辑
降低复杂性思路
- 分离属性查询和关系遍历逻辑
- 复用增量和全量流程
- 关系遍历逻辑是可以组合的: walk_aid2bid, walk_bid2cid -> walk_aid2cid
- 顺流而上/逆流儿下逻辑是可以复用的: walk_aid2bid -> walk_bi2daid
- …
(略)
数据存储方案选择
没有一种数据存储可以满足所有场景 避免试图用工具解释问题 (手里有锤子, 所有的问题都是钉子)
数据存储分野
- MySQL / … (RDB类): 基础数据链路关系, 其他数据存储的来源, 禁止线上统计查询
- ElasticSearch: 文本搜索
- 注意不能做正确的多关系条件筛选逻辑: 该广告出现在”北京-今日头条” / “广州-腾讯新闻”, 不应该通过”北京-腾讯新闻”筛选出来
- Clickhouse: 线上用聚合统计宽表, 禁止JOIN
- Presto / … (Hive系): 离线分析
- OTS / HBase / DynamoDB / …: 大表, 稀疏字段类需求
结论
目前只是抛出数据同步问题, 暂无好的解决方案.
对策:
- 消灭问题, 不做数据同步, 查时关联
- 针对特化的业务场景权衡决策
- 如果需要冗余, 避免过于匹配业务具体需求的数据关系冗余, 做到满足性能的阶段, 保持一定的灵活性/预见性, 避免需求小变更导致大返工
- 在数据同步维护的复杂性 和 查询复杂性 和性能SLA中找折衷
- 简单处理, 部分关系当作事实看待, 简单只做”顺流而上”, 但至少要认识到问题, 遇到问题时会解决, 遇到数据不一致问题显著时清楚要做”逆流而下”
- 直面惨淡的人生, 面对问题, 代码层面统一姿势, 降低目前解决严格一致数据同步脚本的复杂度/维护成本
“图穷匕见”说各组分工
- 采集: (尽可能多的)记录事实
- 数据/算法: 事实推导关系, 努力提升主体(E)识别的唯一性, 提高关系(R)识别率及正确性
- 业务: 数据同步, 短路关系, 人工维护物化视图
(略)