需求背景
在企业后台管理系统中,充斥着各种对象关联关系,联表查询必不可少。很多CRM项目在发展初期,背后的列表查询SQL都是各种join,主打一个敏捷开发,快速上线。在项目的访问量、数据量比较小的时候,即便join四五张表,确实也不会有什么查询压力。但随着业务的发展,C端用户量逐渐增加,各种数据库表也在不断膨胀,原先200ms内就能响应的列表查询接口,要loading个几秒甚至几十秒才能返回数据。即便拆分查询SQL语句,也只能优化部分查询条件的检索速度,很难彻底地解决慢查询问题,这时就需要进行读写分离改造了。
方案选型
个人计划的读写分离方案有两种
- 将所有需要联表查询的表数据实时同步至OLAP数据库,例如Clickhouse和Kudu,在OLAP数据库上进行联表查询。
1 | sql复制代码优点:改造成本最小,只需要在OLAP数据库中建个一模一样的表,Binlog增量同步就完事,查询SQL几乎不用变,替换数据库连接配置即可。 |
- 将多个具备关联关系的数据源,异构成一个聚合对象,同步至Elasticsearch,使用nested嵌套查询。
1 | 复制代码优点:可以存储数组、嵌套对象,检索方式比较多样,全文检索功能强大。 |
最终选用方案:异构同步至Elasticsearch
- 为什么:没为什么,我真的很想用OLAP解决这需求,但是公司的公共数据库部门在OLAP这方面没啥投入,大部分精力都投入在MySQL分片库、OLTP、Hive、Elasticsearch等地方,只有MySQL分片库、Elasticsearch和一个半自研的NewSQL具备核心保障级别。我甚至都没见到一个像样的Clickhouse建库工单 T-T。
技术实现
数据源梳理
首先,我们的对象关系,类似于下图所示(只是举个例子,实际业务肯定不长这样哈)
会有一个主表,对应多个关联表。关联表小的有几百条数据,多的有几千万条数据,尤其是标签表,一个用户头上挂着几十个标签都很正常,有一百万的用户就有几千万的标签关系。且存储的数据源各不相同,有些是自己部门的,有些是其他部门的,各个数据源情况类似于下图所示
反正就是各式各样,五花八门
对象异构
数据聚合,以用户对象为根,六合一,组成嵌套对象
同步方案
- 用户主表增量同步:咱们公司有开箱即用的Binlog同步平台,我只管从RocketMQ里消费Binlog事件就ok。
- 用户主表全量同步:半夜开个定时任务,扫表,定时更新一些很久没有人操作过的历史数据(有些用户可能很久都没有信息、订单、好友关系变更,但是他的标签变了,得同步这批冷数据的关联标签)。
- 用户关联数据同步:接收到一条用户表Binlog事件时,按用户id查出所有关联数据,聚合在一起塞进Elasticsearch。
- 分布式锁:因为Elasticsearch数据保存后不能立马查到,如果在数据插入Elasticsearch后,马上又来一条更新事件,这时是没法从Elasticsearch中查出刚插入的数据的,然后该更新事件就会被判定为是新增事件,导致重复新增两条一样的记录,因此需要一个利用ETCD或者Redis的分布式锁功能,在新增时加把锁,避免重复新增同一条记录。
完整流程
简单来说,同步流程以用户主表更新为关键触发点。当用户加好友、下单、编辑个人信息时,都会触发用户表记录更新(可以理解为,想要触发用户数据同步逻辑,就得更新用户表,统一收口,方便后续迭代维护),此时会将本次用户更新操作的binlog数据抛入消息队列,当数据同步服务接收到binlog数据后,查出所有关联数据,聚合写入ES。
End
本文转载自: 掘金