前面文章介绍了我们开源的redis实时同步工具《我们开源啦》,今天,我们来一起了解下如何实现一个实时数据同步工具。
为什么要自研,使用开源方案不行吗?
-
对稳定性影响更小 -
复制优先级:可用指定优先从从库进行复制或主库复制 -
对RDB中的大key进行拆分同步 -
数据安全性 -
本地缓存支持数据校验 -
对redis限制更少 -
支持源和目标端不同的redis部署方式 -
兼容源和目的redis不同版本 -
运维更加友好 -
数据过滤:可以对某些正则key,db,命令等进行过滤 -
API:可以通过http API进行运维操作,如强制全量复制,同步状态,暂停同步等等 -
监控:监控指标更丰富,如时间与空间维度的复制延迟指标
-
每个节点都会有3个同步模块(因为源redis有3个分片),互为主备,P2P对等结构,将工具故障带来的影响降到最低。 -
两个同步节点各自3个同步模块会独立进行选举,选出最新数据的节点为leader,其他节点为followers,followers从leader同步数据;如果leader节点故障,则最新数据的follower选举成为leader。
同步模块的内部实现
-
输入端:伪装成redis slave,从源redis节点(主节点或从节点)同步数据 -
通道端:即本地缓存,缓存RDB和AOF数据 -
输出端:从本地缓存读取数据写入到目标端redis主库
|
集群
高可用
-
协商式: -
最新数据投票 -
适用于有状态服务 -
选举时间长 -
抢占式: -
先到先服务 -
适用于无状态服务 -
选举时间短
-
半状态化:有状态(缓存数据),但数据并不重要 -
选举时间:越快越好
那么redis-GunYu该用哪种方式呢?
为什么要这么设计呢?
-
同步工具主从节点之间的数据延迟有多大? -
是快速继续服务重要还是最新数据重要? -
1秒钟能从redis源端同步多少数据?
-
能够减少选举时间,快速选出leader -
同步工具主从延迟很低;就算抢占式选举出的leader数据不是最新的,由于同步的速度非常快,也会马上追上最新源端数据
-
如果选出的leader数据落后很多,那么其他拥有更新数据的节点连接到leader后,leader会将领导权移交给最新数据的follower。所以如果新leader无法在瞬间追上源redis节点的数据,也会被其他节点抢夺leader权,这样就降低了源和目的redis数据差异
关于脑裂
拓扑感知
-
如前面所讲,每一个源redis节点,都会有一个对应的同步模块,扩容、缩容都会增加redis节点,那么同步模块就要相应增加或减少 -
如果用户指定从源redis集群的主节点进行同步,主从切换后,我们要能够切换到新主节点上。 -
我们的一致性策略是和拓扑相关的,如果拓扑结构变化,要检查是否需要切换一致性策略。
本地缓存
先看看本地缓存的几个需求
-
数据管理
-
RDB和AOF数据的管理
-
读写RDB和AOF数据
-
超过容量限制,回收缓存数据
-
数据监控
-
读、写监控指标
-
数据安全
-
数据校验
-
损坏数据处理
-
数据持久化策略
缓存数据的组织
垃圾回收与读写
持久化策略
-
由操作系统决定
-
定时持久化和脏数据大小满足一个条件即持久化
-
每次写入都持久化
数据校验
输入端
为了支持断点续传,所以要记录已经同步数据的位置(称为偏移量, offset),下次启动同步流程,则接着上次已同步的位置继续同步。
而这个同步位置,记录在目标redis节点上,每次启动要从目标redis获取偏移量,然后和本地缓存进行比较,具体比较过程如下:
-
如果 目标offset < 本地缓存最老数据(left) :丢弃本地缓存,使用目标端偏移量进行同步 -
如果 本地缓存最老数据(left) < 目标offset < 本地缓存最新数据(right) :使用本地缓存最新偏移量进行同步 -
如果 本地缓存最新数据(right) < 目标offset :丢弃本地缓存,使用目标端偏移量进行同步
实际情况要复杂一些,需要考虑一些其他因素,如同步偏移量是否在源端reids的缓存区,复制ID变化问题,本地缓存数据与目标端redis数据的间隔过大,本地缓存垃圾回收等问题。 |
输出端
数据解析
RDB数据格式 :
RDB数据布局如下图,
-
头部区域描述魔数,版本等
-
接下来是每个DB的key value分布
-
最后是尾部,用来记录CRC值
AOF数据格式
第一个字符是类型,再是数据大小,最后是数据本身。
如下图SET操作日志
-
*表示数组,3表示有3个元素
-
$表示字符串,3表示字符串长度
所以这条日志是描述一个数组,有3个元素,分别表示为SET、key1、val1。
数据处理
数据处理主要对数据进行过滤,替换,如
-
按照命令过滤
-
按照前缀key过滤
-
过滤特殊命令:如过滤cluster,flushdb等命令(slot不对称flushdb不应该进行回放)
后面还会支持插件这种更为灵活的处理方式。
数据回放
-
如何保证一致性? -
如何优化回放性能? -
如何批量进行回放 -
以什么方式将数据回放到目标端redis -
大key怎么回放
如何保证一致性
当源和目的redis集群的槽位不对称时,整个目标redis集群维护一个偏移量,包含源redis节点的数据偏移量;
将发往同一个目标redis节点的命令打包一起发送,而偏移量则定期更新。
所以,如果同步程序崩溃,则有可能导致多回放数据,对于非类幂等操作则会导致数据不一致;当然正常退出是不会有这种问题的。
实际情况还考虑了一些其他场景,如偏移更新时机,回放性能,重定向和错误处理等等。 |
如何提高回放性能
-
对于RDB,数据之间是没有依赖关系的,则可以并发回放数据 -
而AOF的操作日志,数据之间会有依赖关系,无法进行并发回放,必须保证回放的有序性。但是我们可以将其打包,一起发送到目标端,以减少网络传输带来的消耗。打包发送同时考虑打包策略,打包多少命令,打包命令的时间跨度,字节数,同时要考虑和偏移更新、事务的处理要保持兼容。不同的业务对延迟的敏感程度不一样,所以这些都要求可配置,以满足不同需求。
如何进行回放
-
某些特殊数据结构,如FUNCTION -
源和目标redis版本不一致,对于某些数据结构不兼容 -
数据结构太大
可观测性
写在最后
代码仓库地址 :https://github.com/mgtv-tech/redis-GunYu
原文始发于微信公众号(技术闲聊吧):如何设计一个实时数据同步系统
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/269602.html