解决关系型数据库的数据同步难题
Overview
关系型数据库,以OLTP(Online Transaction Process)形式在存储和实体逻辑关联领域带来的价值毋庸置疑,然而在搜索,缓存,数据分析等场景却并不见长。
关系型数据库作为企业重要的数据源,为了丰富数据的应用场景,由此产生了数据同步的需求。
下文以搜索场景的数据同步为例说明
Solutions
1、同步双写
以搜索系统建设的场景为例,业务侧应用中触发的dml事件,同时应用于rdb和Search,将dml的事件上下文以同步的形式分发到需要扩展的目标数据源,以满足Search的数据要求。
问题:
1)能力复用:数据同步的能力复用性不够。
2)耦合性:业务的侵入性较大,开发阶段和运行时,对应用侧都有额外的开销。
3)一致性:双写成功率的保证,需要额外的工作去实现。
2、批量更新
通过可调度的批量任务,拉取并加工数据,推送到指定的异构系统(Search),
以满足对数据需要。
问题:
1)同步时延:同步延时受scheduled job的周期影响,需要根据场景定制。
2)耦合:同步任务的开发质量影响rdb的资源。
3)可复用的能力不够,不同场景需要多次介入;比如:同步的目标数据源是Cache、Queue、Hive。
Syner
我们的方案,整体采用事件驱动模式,设计如下:
binlog-parser:通过同步binlog-parser(databus,canal)模拟rdb从库解析binlog,实时感知dml操作的变化,将rdb的binlog当成是事件源,解决同步延时的问题。
Queue:事件驱动模式中,很重要的一部分是事件存储,我们通过MQ来做的实现。存储基于数据库表的维度来做Queue的切分,以保证表级别的事件顺序。
Syner:核心能力是将具体的事件映射到N个目标异构系统(Search,Cache…)。简单的逻辑映射如下,左侧表示事件的来源,其本质是rdb表的行的变更(binlog基于row模式解析),右侧表示目标系统(异构系统的数据建模考虑,留待其他文章讨论)。
Syner的工作过程:
1)获取存储层的事件信息
2)过滤异构系统不关注的事件信息
3)基于事件的数据模型,转换事件的数据模型(普遍情况下与目标异构系统数据模型存在差异,源事件需要一个转换的过程)以满足异构系统的模型要求
4)基于转换后的数据,下沉到异构系统
Syner的行为抽象:
1)连接存储层获取事件
2)事件分发到应用场景,并依据“规则”转换
3)下沉数据到异构系统
规则引擎模式是流程自动化
基于抽象,在步骤相对确定的情况下,通过规则引擎模式来表达整个过程,可以使得整体流程趋于自动化,达到降低开发和接入成本的目标。
确定以规则引擎的建设思路后,工作内容分为了以下两个大的部分:
定义规则
1)subscribe:表示当前规则“感兴趣”的事件源范围,细化到field级别
2)mapping:包含两个部分:
a.事件源与目标异构系统之间的数据模型映射;
b.事件的数据模型,普遍情况下与目标异构系统数据模型存在差异,需要有一个转换的过程,这个过程通过业务侧提供的api来实现;api的设计目标有两个:其一、将数据转换的过程从Syner脱离出来,业务侧自由定制,提高灵活性;其二、api的去向也是一种默认的sink方向(此处结合下文提到的“命令模式”理解)
3)sinker:表示需要数据需要被分发的目标异构系统。
实现引擎
相对上一步来说,解析规则是引擎的一个部分工作内容,另外还需考虑:
1)规则本身的生命周期的管理
2)可用性:错误隔离,资源分配(比如:线程数量,队列大小,网络IO timeout 等等)
3)吞吐量:数据一致性问题对吞吐量的影响
4)API动态集成:对转换API的动态集成与管理
5)同步过程管理:全量、增量、修复、校验...
6)监控、告警
整体架构如下
admin:负责规则生命周期的管理,同步过程的管理(未来可包括引擎本身的动态调优)
api-srv:负责提供指定的api接口实现,并注册到服务发现集群
如何保证数据一致性?
1)binlog本身的记录方式基于时间的全局序列事件;
2)Queue,存储以表维度的拆分binlog的全局序列,保证局部有序即可;用table hash mode 可能存在Queue数据量倾斜问题,未来可通过一致性hash等的思路来解决数据平衡的问题;
3)Syner作为事件驱动模型的dispatcher部分,为了提高吞吐量,使用的是异步多线程模型,未解决数据的不一致问题,引入趋势递增的序列号(原理借鉴snowflake)。趋势递增序列号为什么能解决这个问题?binlog本质上还是数据的snapshot,而且在到达Queue之前是严格的时间顺序,比如某一行数据x=a,第一次update之后 x=b,第二次update之后x=c,第一次update事件标记序列号为1,第二次标记为2(趋势递增必定>=2),从数据的最终一致性来理解,编号2的事件才是数据的最终版本。因此事件在最终消费时,只消费编号>=上一次的事件即可解决多线程消费下的数据一致问题。基于这个思路,还可以优化事件容量,原理类似Redis的AOF重写,一段时间内多个事件可以聚合为一个最终事件。
针对高可用的工作内容
1)binlog-parser的集群多节点主备,主备节点自动切换;
2)Syner集群在规则引擎之间的相互隔离;
3)enrich api的异步使用(结合Queue)
4)enrich api的response的使用命令模式(SAVE、UPDATE、DELETE、NONE、ASYNC)编排sink行为
5)同步过程中各环节数据的修复能力(自动和手动形式结合)
命令模式
这是一个整体的tradeoff,Syner可在不关注invoke的结果的情况下,将目标数据源的操作行为全部交由api-srv自行处理,如下图的一种实现方案(实际并未采用)
tradeoff
1)成本:中等规模团队首要发展,仍以业务迭代为主,且对于异构数据源API学习成本偏高,投入产出相对不高
2)质量:API的实现质量因人而异
3)安全:目标异构源的安全性(对一个中小型团队来说仍然需要不少工作内容)。
命令模式(指令)的划分
Syner统一实现对于目标异构源的操作(写操作无外乎增、删、改),invoke api的返回结构中包含两个部分:1)转换后的数据内容;2)将数据内容应用于目标数据源的操作指令。
SAVE、UPDTE、DELETE 理解为针对写的常规操作;
NONE:表示当前事件,无需执行任何后续的操作;
ASYNC:表示异步模式,收到当前请求后立即结束后续流程;
NONE与ASYNC的区别在于:NONE表示不存在任何后续的操作行为,ASYNC表示收到当下的请求返回,即可结束,但是会存在异步线程处理数据结果。
最终效果
通过管理端(admin)对规则的定义,Syner自动感知规则生命周期变化,并动态管理规则引擎无需重启。通过已经注册好的enriche api实现数据模型的转换。同时在admin中管理同步过程。
对于用户来说,整体过程的感知只有以下两个步骤:
1)开发enriche api
2)可视化的规则配置