2019/12/14

如何处理业务上的较多表对接

handle-lots-of-data-tables
最近这2个月来的主要业务是和数据组打交道,将他们提供的各个维度的数据表(实质上是基于底层的各种事件已经计算并聚合过一层的view,这边统一用表表述)在接口层面进行封装为前端提供接口。简单写文回顾加总结一下。

场景描述

和一般的重查询的业务基本相同,但有个比较明显的问题是表的数目比较可观。
现在的业务已经涉及到了几百张表,主要是接入的领域,以及领域下的各种指标累加起来比较多。
举一个业务无关例子,比如用户访问论坛的行为,需要展示用户的发帖数,编辑数,回复数,查看数,点赞数这几个指标,功能上分为了单个用户的指标汇总,单个用户指标趋势和某个指标的具体实体排行榜(比如查看数是展示具体的帖子数),时间维度分为日,周,月,季度和年。
数据组会根据业务需要的指标给出对应的表,但他们给的表不会直接业务相关,所以可能发帖,编辑在一张表里,回复在另一张表里,查看和点赞在第三张表里,这边获取一个单用户指标的汇总就需要查3类表,其次因为有去重的操作,数据组会选择将不同的聚合时间段分成不同的表,于是根据时间的维度在这个方向上又会有5类表(当然时间段内可累计的话就直接日期表累加就好了,不可累计指的是比如在一周内7天都看了同一个帖子,那按每天累加的话是7,但是按一周内看的帖子应该是1),指标可能有不同的筛选维度,不同的维度也是不同类的表,比如可能需要按帖子主题,帖子分类,帖子用户所在地区这3个维度分类,如果维度间有交叉那所需要的表就更多,主题x分类,主题x所在地,分类x所在地,主题x分类x所在地等等,假设没有交叉,那就是3类维度表,那总的表的数量就是3 * 5 * 3 = 45,这只是单个用户的汇总数据一个功能。
对于上面为什么不同筛选维度要有不同的表,而不是全部放在一张表里,这其实是数据组底层实现的选择。一来是数据量过大底层会聚合过多的物理表,对之后的扩展和维护是个很大的挑战;另一方面则是数据查询的意义,你只设定了一个筛选维度,那要返回的是选定的筛选维度+所有其他维度的值的汇总数据呢,还是选定维度+所有其他维度的值的行呢(也就是你到底需要的是一个group by distinct sum 还是一个返回所有维度值的行),当然这可以使用类似特殊值的方式来区分;最后一个问题就是之后增加新的筛选维度怎么办,只在一张表里的话新增字段数据会翻倍增长。
如下图,当前只有两个筛选维度,各自都只有1这个值,我们用all这个特殊值来表示筛选项不指定的情况(也就是group by distinct sum的值)。
filter1 filter2 value
all all 1
1 all 1
all 1 1
1 1 1
那增加一个新的filter3会变成如何。
filter1 filter2 filter3 value
all all all 1
1 all all 1
all 1 all 1
1 1 all 1
首先之前的数据要先填充filter3是all的情况,接着要写入filter3的所有值,原来表的大小是(m+1)x(n+1),现在就会变成(m+1)x(n+1)x(z+1),m,n,z是筛选维度的所有值。

解决方式

初期

表的数量已经无法减少,在第一次接入的时候使用了之前的方式,结果变成了实打实的重复劳作机器,经历了漫长的半个月完成了一个领域的接口,但接下来还有更多的领域,以及会有一些额外的短时离线任务和长时离线任务,用这种方式不可控。
但在最开始还是大致摸通了一些规律,比如时间维度拆分的同类表有同样的表名模式,以及内部只有时间字段不同,相同指标系列的表的指标名都是一致的,筛选维度的同类指标表的指标也是一致的只有筛选项不同。

模式化

结合初期发现的规律,基础的解决方式有了一定的雏形。
不需要写每一个表查询,只需要抽取合理的模式。
  • 日期+指标类型决定表名
  • 选择的筛选维度可以确定路由到某张表
  • 同样指标类型的表可以共享字段名等元数据
因为项目安排比较紧的关系,在编码上慢慢往这个方面靠拢,但是还没有抽取出通用的方法。

现在

省略更多中间的探索,直接说下结论。
现在要接入新的已经有比较明确的编码流程。
  1. 元数据抽取和整理(这部可工具化):
    将数据组给的表进行分类和整理,在代码中定义表名,表名模式(比如user_posts_d,user_posts_w,user_posts_m,定义为user_posts_{0}),定义同一类指标的字段名,定义表名和字段名的映射关系。
  2. 写对应router,(筛选项,日期类型)-> 表名,这边要注意这个router是个通用的router,那种只选定一个筛选项,要返回其他所有筛选项值的功能还是单独写的(比如选中一个一级分类,返回不是一级分类的汇总值,而是底下所有二级分类的值)。
  3. 主流程。传入参数 -> 构建filter list -> 选择表名 -> 根据表名-字段映射返回需要filters。
上面举的例子中的45张表就在这一套流程中完成。
时间上有了很大的收缩,之前可能需要2个星期才能接入完毕的一套表,现在大致可以缩短到2天(毕竟还有其他衍生功能要做)。

总结

如上面括号里的,这一套方式其实有很多可以自动化的点,比如根据数据组给到的数据定义自动全面地(不全面的已经有了脚本)生成代码里的元数据定义,甚至router和最后的代码,然后进行微调,在理论上完全可行,至于实践层面可能就需要耗费比较多的精力,而且可能ROI不高,现在已经在用一些脚本自动生成些代码了,完整的工具的必要性和能提高的工作效率。
Written with StackEdit.

没有评论:

发表评论