RedisShake 简介
RedisShake 基本介绍
RedisShake是基于redis-port基础上进行改进的是一款开源的Redis迁移工具,支持Cluster集群的在线迁移与离线迁移(备份文件导入)。数据可平滑迁移,当部署在其他云厂商Redis服务上的Cluster集群数据,由于SYNC、PSYNC命令被云厂商禁用,无法在线迁移时,可以选择离线迁移。
RedisShake 使用背景
RedisShake是一个用于在两个Redis实例之间同步数据的工具,满足非常灵活的同步与迁移需求。Redis实例之间的关系其中可能存在(standalone->standalone),(standalone->Cluster),(Cluster->Cluster)等。目前,比较常用的一个数据迁移工具是Redis-Shake ,这是阿里云Redis和MongoDB团队开发的一个用于 Redis 数据同步的工具。
Redis-Shake 特性概览
- 高性能:全量同步阶段并发执行,增量同步阶段异步执行,能够达到毫秒级别延迟(取决于网络延迟)。同时,我们还对大key同步进行分批拉取,优化同步性能。
- 在 Redis 5.0、Redis 6.0 和 Redis 7.0 上测试
- 支持使用lua自定义过滤规则
- 支持大实例迁移
- 支持restore模式和sync模式
- 支持阿里云 Redis 和 ElastiCache
- 监控体系:用户可以通过我们提供的restful拉取metric来对redis-shake进行实时监控:curl 127.0.0.1:9320/metric。
- 数据校验:如何校验同步的正确性?可以采用我们开源的redis-full-check。
- 支持版本:支持2.8-5.0版本的同步,此外还支持codis,支持云下到云上,云上到云上,云上到云下(阿里云目前支持主从版),其他云到阿里云等链路,帮助用户灵活构建混合云场景。
- 断点续传。支持断开后按offset恢复,降低因主备切换、网络抖动造成链路断开重新同步拉取全量的性能影响。
Redis 主从同步原理
Redis 持久化
Redis有两种持久化机制:RDB和AOF。
RDB是一个经过压缩的二进制文件,通过保存Redis数据库中的键值来记录数据库状态。
AOF通过保存Redis服务器所执行的写命令来记录数据库状态,例如:
RDB和AOF文件可以恢复成一个数据库,继续提供服务。
- Replication
复制:包括全量数据的复制和增量数据的复制。
同步操作:将从服务器的数据库状态更新至主服务器当前所处的数据库状态。Master生成RDB文件,Slave载入。
命令传播操作:主服务器的数据库状态被修改,导致主从服务器的数据库状态出现不一致时,让主从服务器的数据库重新回到一致状态。 Master执行的写命令传送到Slave。
Redis 主从复制
在分布式环境中,数据副本 (Replica) 和复制 (Replication) 作为提升系统可用性和读写性能的有效手段被大量应用系统设计中,Redis也不例外。
在2.8版本之前,主从同步是通过SYNC命令进行的,在2.8版本之后升级到PSYNC命令,下面分别展开介绍。
主从复制 - SYNC
在发送slaveof ip port的时候,它首先会创建两项主服务器套接字的连接,接着向主服务器发送ping命令,目的在于检查套接字的读写状态,或者是检查主服务器是否能够正常地处理命令。
接下来进行身份验证,然后发送端口信息,最后发送SYNC命令,执行同步操作。
在主Master接收到SYNC命令之后,它会执行bgsave在后台生成一个RDB文件,并且使用一个缓冲区记录从现在开始执行所有写命令。当bgsave生成的RDB文件完成了之后,它就发送给从服务器去进行载入。在更新状态完成之后,Master再将记录在缓冲区里面的新命令发送给从服务器,这样从服务器进行执行,主从服务器就保持了一致状态。
- SYNC缺陷
从服务器到主服务器的复制可以分为两种情况,一种就是初次复制,一种就是断线后的重复制。初次复制就是进行建立连接,然后进行全量和增量的同步,它的SYNC可以很好地完成任务。
但对于断线后的重复制,处于命令传播阶段的主从服务器因为网络原因而中断又重连,会再次发送SYNC命令做全量+增量同步,效率较低。
SYNC命令是一个非常耗费资源的操作,资源包括CPU、内存、磁盘、宽带、流量等。
主从复制 - PSYNC
为了解决SYNC在处理断线重复制时候的低效问题,Redis从2.8版本之后开始使用PSYNC命令,它支持完整重同步和部分重同步。
完整重同步和SYNC一样,部分重同步就是在处理断线重新链接之后,主节点只向从节点发送链接断开期间的写命令,它的实现基于以下三部分:
1)复制偏移量(replication offset)
n Master:每次向Slave发送n个字节数据时,就会将自己的offset+n;
n Slave:每次收到Master发送来的n个字节数据时,就会将自己的offset+n;
n 如果主从服务器处于一致状态,那么Master和Slave的Offset总是相同的。
2)复制积压缓冲区(replication backlog)
- Master上维护的每一个固定大小(fixed-size)的FIFO队列,保存着一部分最近传播的写命令。
- Master进行命令传播时,不仅会将命令发送给所有Slave,还会将写命令入队到复制积压缓冲区。
- 复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量;
- Slave重连Master时,会通过PSYNC命令将自己的Offset发送给Master,如果Slave的Offset与Master的Offset不相等,并且Slave的Offset偏移量之后的数据仍存在于replication backlog中,那么Master将对Slave执行部分重同步操作;否则,需要执行完整重同步操作。
3)服务器ID(run ID)
- 每个Redis服务器,Master、Slave、都有run ID自动生成。
- 当Slave对Master进行初次复制时,Master会将自己的run ID传送给Slave,Slave会记录Master的run ID。
- 当Slave重新连接上一个Master时,会发送之前保存的run ID,用于确认是否为同一个Master。
1. PSYNC—fullresync
过程如上图所示,抛去刚开始的建立连接不谈,对于初次复制,它会发送一个“PSYNC ? -1”命令,主动请求主服务器去进行完整的重同步。
主服务器接收到命令之后会返回FULLRESYNC,表示执行完整重同步,下面的步骤就和SYNC是一样的。
2. PSYNC—partial resync
当在增量阶段突然断线,它会再次建立连接。Slave向主服务器发送run ID和offset,run ID就是上一次复制的主服务器的运行ID,offset就是从服务器当前的复制偏移量。接收到命令之后,主服务器会通过这两个参数去判断,如果判断条件满足,就返回CONTINUE,之后发送缓冲区中的那些写命令再去执行,就回到了一致的状态。
RedisShake 基本原理
主要功能
RedisShake主要是支持Redis的RDB文件的解析、恢复、备份、同步四个功能:
- 恢复(restore):将 RDB 文件恢复到目标Redis数据库。
- 备份(dump):将源 Redis 的全量数据通过RDB文件备份起来。
- 解析(decode):读取 RDB 文件,并以 JSON 格式解析存储。
- 同步(sync):支持源redis和目的redis的数据同步,支持全量和增量数据的迁移,支持从云下到阿里云云上的同步,也支持云下到云下不同环境的同步,支持单节点、主从版、集群版之间的互相同步。
- 同步(rump):支持源 Redis 和目的 Redis 的数据同步,仅支持全量迁移。采用scan和restore命令进行迁移,支持不同云厂商不同redis版本的迁移。
注意:如果源端是集群版,可以启动一个RedisShake,从不同的db结点进行拉取,同时源端不能开启move slot功能;对于目的端,如果是集群版,写入可以是1个或者多个db结点。
基本同步模式
基本同步模式流程图
- 支持的Redis形态:
1)Standalone:单源拉取,主从版/单节点
2)Sentinel:从Sentinel获取地址并拉取
3)Cluster:开源Cluster模式
4)Proxy:从Proxy拉取
下面举例说明:
现在要将一个主从版的数据迁移到集群版,可以在配置文件中配置源端信息和目的端信息,同时在配置地址的时候,对于集群版可以配置一个分片的地址,因为它是支持自动发现的。
数据流图
上图是SYNC模式下全量+增量的数据流图,第一次复制向源端发送了之后,源端给它发RDB文件,解析RDB文件放到Pipe中。
它可以通过并发Restore到目的端,同步完成之后就会自动启动增量。增量就是从源端拉取写命令,进行解析过滤,然后再放到目的端,同时还需要接收回复。
全量同步
首先可以看全量同步,DbSyncer维护了连接链路,它是一个结构体,源端是6379,目的端是一个集群,它代表了一个数据链路。
假如说原来配置的是一个三主三从的集群模式,那么就会开启三个DbSyncer,同时有ID对它进行标识。每个DbSyncer管理一个连接,如6379首先是向RDB文件发送过来,然后Loader会解析出一些信息,包括DB、Key、Type等,然后放到Pipe中。然后需要向目的端写入,写入的时候全量模式支持并发。
从Pipe出来后先进行过滤,因为RedisShake支持DB、Key、Slot这些级别的过滤,用户可以设置黑白名单。正常情况下就可以Restore到目的端,然后再接收回复。对于lua ,可以进行script load,也可以设置过滤lua。对于BigKey,需要根据编码形式进行拆分解析。
增量同步
全量同步结束之后会自动开启增量同步,增量同步主要是由四个协程完成。
第一个协程定期从源端获取RedisShake的偏移量。
第二个协程主要是从源端拉取写命令。因为原来是模拟源端的一个从,从执行写命令的时候会发送一条Binlog到RedisShake,RedisShake读到之后交给Decoder,然后会将Binlog解析成一个原生命令,同时还会计算一下偏移量。得到命令后有Key,然后就可以进行过滤,再放到Sendbuffer中,Sendbuffer就是最终要发送到目的端的一些写命令。
第三个协程就是Sender,它执行发送逻辑,从Sendbuffer中取出写命令,并不是立即发送过去,它是和Checkpoint绑成一个事务,一块写入目的端。断点续传的原理是会先放到cache里边,就像是一层缓存,然后触发一定条件之后就去执行sendFunc函数。这是一个事务,它会先去写这些增数据,然后再去修改Checkpoint值。
第四个协程就是接收目的端的回复,可能是写入成功,也可能是写入出错的,主要用于统计。
下面我们模拟发送一个命令,观察会发生什么事情。
首先源客户发送了一个“SET msg hello”,然后源端会以一个Binlog的形式写入到RedisShake。RedisShake读到之后先解析出原生的写命令,就会调用Send函数。Send函数目的端是一个集群,它需要知道自己应该发送到哪个节点上。
因为集群方式有槽概念,RedisShake还维护了 Slot数组,它的 index就是槽的ID,它对应的值就是槽所在的节点上。Node是一个结构体,里边有一个字段address,就是节点的地址。
调用Send函数的时候,首先需要获取到这个Key是应该发送到哪个节点上,会调用getNodeByKey,实际上就是对Key进行一次Hash,调用的是crc16算法。Key会算到它在哪个槽上,然后再通过下面 Slots数组,就知道这个槽是在哪个节点上,然后再调用do把它写入到 node上,写入之后会Receive目的端的回复。然后我们就需要去检查这个回复是什么,如果是有异常的话,就会直接将这个结果返回过去,再由上一层去进行判断。
假如说是move或者是ask,可能会调用回调函数handleMove或handleAsk两个重定向。
MOVE/ASK重定向
1.MOVE重定向
上图为MOVE重定向流程图,因为RedisShake是模拟一个客户端,然后RedisShake向节点写命令时,这个节点也会进行一次Hash去看它是不是应该在写到自身。如果是的话就执行,不是的话就返回一个MOVE。
这个MOVE代表这个Key应该写到这个节点上,因为目的端可能会进行扩容,所以说槽的分布可能是会有变化,RedisShake支持进行扩缩容。收到MOVE的时候,它会提取到目标节点的信息,再向目标节点重新发送键命令。
2.ASK重定向
上方为ASK重定向流程图,主要是发生在Slots迁移的时候,RedisShake向A节点发送键命令的时候,A在同一向里回复一个ASK转向,表示这一次向B去写入,然后RedisShake再向B发送键命令。
断点续传原理
断点续传是RedisShake2.0版本增加的功能,是根据Redis PSYNC协议写的,下方是它的流程图。
如上图所示,目的端开启了断点续传,获取到Checkpoint保存的最大偏移量,然后通过PSYNC指令发送源端,然后在源端去检查是否合法。
如果需要的写指令还在复制缓存区中,它就会回复一个Continue,然后进入增量同步,如果不是的话就会进行全量同步。
主从版的断点续传方案会在每个节点DB上记录一个Checkpoint,名字固定是RedisShake Checkpoint,类型是Hash。
在写数据的时候,Redis是将Checkpoint和数据捆绑成一个事务写入目的端的。
- 讨论两个问题:
1)为什么每个逻辑DB都记录一个Checkpoint?
2)为什么要将Checkpoint和数据捆绑成一个事务写入目的Redis端?
关于问题一,分DB存储的优势就是在刚启动的时候,只需要拉取所有DB的offset,挑取最大的就知道它是在哪个DB上,上一次是在哪个DB,以及同步的最大偏移量。
关于问题二,捆绑封装成一个事务是为了保证一致性。
应用场景
数据同步——SYNC
在SYNC模式下支持全量同步与增量同步,当然它有一个限制,需要源端支持PSYNC/SYNC,在向源端发送SYNC/PSYNC命令的时候,可以收到源端的回复。源端、目的端的形态可以是主从集群/Proxy/Cluster,迁移也可以用于云下到云上,云上到云下这种混合云迁移。
多云厂商之间数据迁移
对于某些云上redis,比如部分云厂商不支持SYNC/PSYNC权限,如何进行行迁移?
RedisShake对于这种场景也做了支持,比如绕过SYNC和PSYNC的同步方式。它是以Scan的方式从源端Redis获取到全量数据,再写入到目的端,实现数据迁移。
比如有一个fetcher,它会先Scan一定的数据量, 然后配置COUNT,然后将收到的Key给DUMP下来放到PIPE中,然后writer从PIPE中取到Restore到目的端,同时它也有BigKey的优化。
多活
多活通常是用于解决因地域网络传输层面带来的问题,也就是异地多活。比如说业务层面想要在北京机房写入一条数据,要求在上海机房也能够读到,同样反过来也可以,这样会需要做到一个多活。
上图给出来的是一个根据RedisShake创建多活的方案,是一种伪多活而不是真正的多活。
用户需要编写一个Proxy进行流量分发,比如对a或者是b库的写操作都分发在左边的DB,对c库的写操作都分发到右边的DB。RedisShake提供了按库过滤的功能,可以配置源库到目的库的Shake链路值,同步ab,然后目的库到源库的只同步c,这样就解决了一个环形复制的问题。
数据备份与恢复
数据备份使用dump功能,就可以直接备份一份rdb,将这些rdb根据时间命名。
- 数据恢复restore
- 自建Redis迁移上云
- 云上数据迁移到云下
- 将数据库恢复至之前的状态,数据回滚
- 配合Filter使用,只恢复部分Key
RDB文件解析decode
- RDB文件解析 decode
1)读取RDB文件,以JSON格式解析存储
2)可以进行Key分析
- 高版本向低版本迁移
源端版本大于目的端版本,比如源端是4.0,目的端2.8,某些数据结构格式已经修改导致无法同步,可以通过配置绕过这个限制。
- 扫描迁移 rump
以SCAN的方式从源端Redis获取全量数据,写入到目的端,实现数据迁移。
- 对于不支持sync/psync权限的迁移
- 跨云迁移
全球分布式缓存—全球级联同步场景
假如用户业务遍布全球,有很多个数据中心,之间的数据迁移也可以用RedisShake,它支持全球级联同步,方便用户进一步扩大自己的业务。
检验迁移后的数据
redis-full-check是用来检验两个Redis数据是否一致的工具。
通常在用RedisShake进行数据迁移之后,可以用这个工具进行校验,它的原理主要是通过全量对比源端和目的端Redis中的数据方式来进行数据校验。
如图可以看到,比较的方式是内部多轮次进行比较,每次比较都会先抓取比较Key。第一轮是从源端进行抓取,后面从Sqlite中进行抓取。抓取之后如果存在有差异的部分,会存在SqliteDB中,后面的抓取直接去更新Sqlite中的数据就可以了。
参考文档:
https://developer.aliyun.com/article/783138
https://www.cnblogs.com/liboware/p/17032487.html
- 本文固定链接: http://www.jiagou.cc/758/
- 转载请注明: 摘星怪 于 架构迷 发表