Redis数据迁移

Redis常见的数据迁移方式有以下三种:RDB迁移、AOF迁移、主从复制。

       从广义上来说,迁移属于同步中的一种模式,同步更加侧重于增量,迁移更加侧重于全量。
  使用RDB和AOF文件可以实现跨网络隔离的数据迁移,但是无法做到增量迁移。主从复制可以实现增量迁移但是无法跨网络环境。另外,在过去对于数据迁移后的校验缺乏良好的手段。

在2.8版本之前,主从同步是通过SYNC命令进行的,在2.8版本之后升级到PSYNC命令,下面分别展开介绍。

主从复制 - SYNC 

1.png在发送slaveof ip port的时候,它首先会创建两项主服务器套接字的连接,接着向主服务器发送ping命令,目的在于检查套接字的读写状态,或者是检查主服务器是否能够正常地处理命令。

接下来进行身份验证,然后发送端口信息,最后发送SYNC命令,执行同步操作。

1.png

在主Master接收到SYNC命令之后,它会执行bgsave在后台生成一个RDB文件,并且使用一个缓冲区记录从现在开始执行所有写命令。当bgsave生成的RDB文件完成了之后,它就发送给从服务器去进行载入。在更新状态完成之后,Master再将记录在缓冲区里面的新命令发送给从服务器,这样从服务器进行执行,主从服务器就保持了一致状态。

  • SYNC缺陷1.png

从服务器到主服务器的复制可以分为两种情况,一种就是初次复制,一种就是断线后的重复制。初次复制就是进行建立连接,然后进行全量和增量的同步,它的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总是相同的。

1.png

2复制积压缓冲区(replication backlog)

  • Master上维护的每一个固定大小(fixed-size)的FIFO队列,保存着一部分最近传播的写命令。
  • Master进行命令传播时,不仅会将命令发送给所有Slave,还会将写命令入队到复制积压缓冲区。
  • 复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量;
  • Slave重连Master时,会通过PSYNC命令将自己的Offset发送给Master,如果Slave的Offset与Master的Offset不相等,并且Slave的Offset偏移量之后的数据仍存在于replication backlog中,那么Master将对Slave执行部分重同步操作;否则,需要执行完整重同步操作。

1.png

3服务器ID(run ID)

  • 每个Redis服务器,Master、Slave、都有run ID自动生成。
  • 当Slave对Master进行初次复制时,Master会将自己的run ID传送给Slave,Slave会记录Master的run ID。
  • 当Slave重新连接上一个Master时,会发送之前保存的run ID,用于确认是否为同一个Master。

PSYNC—fullresync

1.png

对于初次复制,它会发送一个“PSYNC ? -1”命令,主动请求主服务器去进行完整的重同步。

1.png

主服务器接收到命令之后会返回FULLRESYNC,表示执行完整重同步,下面的步骤就和SYNC是一样的。

PSYNC—partial resync

1.png

当在增量阶段突然断线,它会再次建立连接。Slave向主服务器发送run ID和offset,run ID就是上一次复制的主服务器的运行ID,offset就是从服务器当前的复制偏移量。接收到命令之后,主服务器会通过这两个参数去判断,如果判断条件满足,就返回CONTINUE,之后发送缓冲区中的那些写命令再去执行,就回到了一致的状态。

redis-shake

https://github.com/alibaba/RedisShake/releases

1.png

     redis-shake是阿里基于redis-port基础上进行改进的一款产品。它支持解析、恢复、备份、同步       四个功能。
  redis-shake解决了下面四个问题:

  1. 源库目标库版本不一致
  2. 为实现平滑迁移,要求能做增量同步
  3. 能快速实现多库批量同时迁移
  4. 迁移速度非常快,基本实现秒级迁移

    恢复restore:将RDB文件恢复到目的redis数据库。
    备份dump:将源redis的全量数据通过RDB文件备份起来。
    解析decode:对RDB文件进行读取,并以json格式解析存储。
    同步sync:支持源redis和目的redis的数据同步,支持全量和增量数据的迁移,支持从云下到阿里云云                    上的同步, 也支持云下到云下不同环境的同步,支持单节点、主从版、集群版之间的互相                      同步。

                      需要注意的是,如果源端是集群版,需要搭建多条链路从不同的db结点进行拉取,

                      同时源端不能开启move slot功能;对于目的端,如果是集群版,写入可以是1个或者多个                      db 结点。
    同步rump:支持源redis和目的redis的数据同步,仅支持全量的迁移。采用scan和restore命令进行迁                       移,支持不同云厂商不同redis版本的迁移。

     redis-shake的基本原理就是模拟一个从节点加入源redis集群,首先进行全量拉取并回放,然后进行增量的拉取(通过psync命令)。
    _

支持的Redis形态:

1)Standalone:单源拉取,主从版/单节点

2)Sentinel:从Sentinel获取地址并拉取

3)Cluster:开源Cluster模式

4)Proxy:从Proxy拉取

如果源端是集群模式,只需要启动一个redis-shake进行拉取,同时不能开启源端的move slot操作。

如果目的端是集群模式,可以写入到一个结点,然后再进行slot的迁移,当然也可以多对多写入。
  目前,redis-shake到目的端采用单链路实现,对于正常情况下,这不会成为瓶颈,但对于极端情况,qps比较大的时候,此部分性能可能成为瓶颈,后续我们可能会计划对此进行优化。另外,redis-shake到目的端的数据同步采用异步的方式,读写分离在2个线程操作,降低因为网络时延带来的同步性能下降。

       全量同步阶段并发执行,增量同步阶段异步执行,能够达到毫秒级别延迟(取决于网络延迟)。同时,我们还对大key同步进行分批拉取,优化同步性能。

      用户可以通过我们提供的restful拉取metric来对redis-shake进行实时监控:curl 127.0.0.1:9320/metric

数据流图

1.png

图是SYNC模式下全量+增量的数据流图,第一次复制向源端发送了之后,源端给它发RDB文件,解析RDB文件放到Pipe中。

它可以通过并发Restore到目的端,同步完成之后就会自动启动增量。增量就是从源端拉取写命令,进行解析过滤,然后再放到目的端,同时还需要接收回复。

全量同步

1.png

首先可以看全量同步,DbSyncer维护了连接链路,它是一个结构体,源端是6379,目的端是一个集群,它代表了一个数据链路。

假如说原来配置的是一个三主三从的集群模式,那么就会开启三个DbSyncer,同时有ID对它进行标识。每个DbSyncer管理一个连接,如6379首先是向RDB文件发送过来,然后Loader会解析出一些信息,包括DB、Key、Type等,然后放到Pipe中。然后需要向目的端写入,写入的时候全量模式支持并发。

从Pipe出来后先进行过滤,因为RedisShake支持DB、Key、Slot这些级别的过滤,用户可以设置黑白名单。正常情况下就可以Restore到目的端,然后再接收回复。对于lua ,可以进行script load,也可以设置过滤lua。对于BigKey,需要根据编码形式进行拆分解析。

增量同步

1.png

全量同步结束之后会自动开启增量同步,增量同步主要是由四个协程完成。

第一个协程定期从源端获取RedisShake的偏移量。

第二个协程主要是从源端拉取写命令。因为原来是模拟源端的一个从,从执行写命令的时候会发送一条Binlog到RedisShake,RedisShake读到之后交给Decoder,然后会将Binlog解析成一个原生命令,同时还会                    计算一下偏移量。得到命令后有Key,然后就可以进行过滤,再放到Sendbuffer中,Sendbuffer就是最终要发送到目的端的一些写命令。

第三个协程就是Sender,它执行发送逻辑,从Sendbuffer中取出写命令,并不是立即发送过去,它是和Checkpoint绑成一个事务,一块写入目的端。断点续传的原理是会先放到cache里边,就像是一层缓存,然                      后触发一定条件之后就去执行sendFunc函数。这是一个事务,它会先去写这些增数据,然后再去修改Checkpoint值。

第四个协程就是接收目的端的回复,可能是写入成功,也可能是写入出错的,主要用于统计。

MOVE/ASK重定向

1.png

上图为MOVE重定向流程图,因为RedisShake是模拟一个客户端,然后RedisShake向节点写命令时,这个节点也会进行一次Hash去看它是不是应该在写到自身。如果是的话就执行,不是的话就返回一个MOVE。

这个MOVE代表这个Key应该写到这个节点上,因为目的端可能会进行扩容,所以说槽的分布可能是会有变化,RedisShake支持进行扩缩容。收到MOVE的时候,它会提取到目标节点的信息,再向目标节点重新发送键命令。

1.png

上方为ASK重定向流程图,主要是发生在Slots迁移的时候,RedisShake向A节点发送键命令的时候,A在同一向里回复一个ASK转向,表示这一次向B去写入,然后RedisShake再向B发送键命令

断点续传原理

断点续传是RedisShake2.0版本增加的功能,是根据Redis PSYNC协议写的,下方是它的流程图。

1.png

如上图所示,目的端开启了断点续传,获取到Checkpoint保存的最大偏移量,然后通过PSYNC指令发送源端,然后在源端去检查是否合法。

如果需要的写指令还在复制缓存区中,它就会回复一个Continue,然后进入增量同步,如果不是的话就会进行全量同步。

主从版的断点续传方案会在每个节点DB上记录一个Checkpoint,名字固定是RedisShake Checkpoint,类型是Hash。

1.png

在写数据的时候,Redis是将Checkpoint和数据捆绑成一个事务写入目的端的。

  • 讨论两个问题:

1)为什么每个逻辑DB都记录一个Checkpoint?

2)为什么要将Checkpoint和数据捆绑成一个事务写入目的Redis端?

关于问题一,分DB存储的优势就是在刚启动的时候,只需要拉取所有DB的offset,挑取最大的就知道它是在哪个DB上,上一次是在哪个DB,以及同步的最大偏移量。

关于问题二,捆绑封装成一个事务是为了保证一致性。

 配置说明

# this is the configuration of redis-shake.

# id
id = redis-shake
# log file,日志文件,不配置将打印到stdout
log_file =

# pprof port
system_profile = 9310
# restful port,查看metric端口
http_profile = 9320

# runtime.GOMAXPROCS, 0 means use cpu core number: runtime.NumCPU()
ncpu = 0

# parallel routines number used in RDB file syncing.
parallel = 4

# input RDB file. read from stdin, default is stdin ('/dev/stdin').
# used in `decode` and `restore`.
# 如果是decode或者restore,这个参数表示读取的rdb文件
input_rdb = local_dump

# output RDB file. default is stdout ('/dev/stdout').
# used in `decode` and `dump`.
# 如果是decode或者dump,这个参数表示输出的rdb
output_rdb = local_dump

# source redis configuration.
# used in `dump` and `sync`.
# ip:port
# 源redis地址
source.address = 127.0.0.1:20441
# password.
source.password_raw = kLNIl691OZctWST
# auth type, don't modify it
source.auth_type = auth
# version number, default is 6 (6 for Redis Version <= 3.0.7, 7 for >=3.2.0)
source.version = 6

# target redis configuration. used in `restore` and `sync`.
# used in `restore` and `sync`.
# ip:port
# 目的redis地址
target.address = 10.101.72.137:20551
# password.
target.password_raw = kLNIl691OZctWST
# auth type, don't modify it
target.auth_type = auth
# version number, default is 6 (6 for Redis Version <= 3.0.7, 7 for >=3.2.0)
target.version = 6
# all the data will come into this db. < 0 means disable.
# used in `restore` and `sync`.
target.db = -1

# use for expire key, set the time gap when source and target timestamp are not the same.
# 用于处理过期的键值,当迁移两端不一致的时候,目的端需要加上这个值
fake_time =

# force rewrite when destination restore has the key
# used in `restore` and `sync`.
# 当源目的有重复key,是否进行覆写
rewrite = true

# filter db or key or slot
# choose these db, e.g., 5, only choose db5. defalut is all.
# used in `restore` and `sync`.
# 支持过滤db,只让指定的db通过
filter.db =
# filter key with prefix string. multiple keys are separated by ';'.
# e.g., a;b;c
# default is all.
# used in `restore` and `sync`.
# 支持过滤key,只让指定的key通过,分号分隔
filter.key =
# filter given slot, multiple slots are separated by ';'.
# e.g., 1;2;3
# used in `sync`.
# 指定过滤slot,只让指定的slot通过
filter.slot =

# big key threshold, the default is 500 * 1024 * 1024. The field of the big key will be split in processing.
# 我们对大key有特殊的处理,此处需要指定大key的阈值
big_key_threshold = 524288000

# use psync command.
# used in `sync`.
# 默认使用sync命令,启用将会使用psync命令
psync = false

# enable metric
# used in `sync`.
# 是否启用metric
metric = true
# print in log
# 是否将metric打印到log中
metric.print_log = true

# heartbeat
# send heartbeat to this url
# used in `sync`.
# 心跳的url地址,redis-shake将会发送到这个地址
heartbeat.url = http://127.0.0.1:8000
# interval by seconds
# 心跳保活周期
heartbeat.interval = 3
# external info which will be included in heartbeat data.
# 在心跳报文中添加额外的信息
heartbeat.external = test external
# local network card to get ip address, e.g., "lo", "eth0", "en0"
# 获取ip的网卡
heartbeat.network_interface =

# sender information.
# sender flush buffer size of byte.
# used in `sync`.
# 发送缓存的字节长度,超过这个阈值将会强行刷缓存发送
sender.size = 104857600
# sender flush buffer size of oplog number.
# used in `sync`.
# 发送缓存的报文个数,超过这个阈值将会强行刷缓存发送
sender.count = 5000
# delay channel size. once one oplog is sent to target redis, the oplog id and timestamp will also stored in this delay queue. this timestamp will be used to calculate the time delay when receiving ack from target redis.
# used in `sync`.
# 用于metric统计时延的队列
sender.delay_channel_size = 65535

# ----------------splitter----------------
# below variables are useless for current opensource version so don't set.

# replace hash tag.
# used in `sync`.
replace_hash_tag = false

# used in `restore` and `dump`.
extra = false

这部分应该是用户最困惑的地方,为了满足用户的灵活配置,目前开放了较多的配置项,但用户一开始使用并不需要管这么多的项。默认sync模式只需要配置以下几个内容:

source.type: 源redis的类型,支持一下4种类型:
standalone: 单db节点/主从版模式。如果源端是从多个db节点拉取就选择这个模式,即便是codis等开源的proxy-db架构。
sentinel: sentinel模式。
cluster: 集群模式。开源的cluster。对于阿里云来说,用户目前无法拉取db的地址,所以此处只能是proxy。
proxy: proxy模式。如果是阿里云redis的集群版,从proxy拉取/写入请选择proxy,从db拉取请选择cluster。正常cluster到cluster同步源端请选择cluster模式,proxy模式目前只用于rump。。
source.address: 源redis的地址,从1.6版本开始我们支持集群版,不同的类型对应不同的地址:
standalone模式下,需要填写单个db节点的地址,主从版需要输入master或者slave的地址。
sentinel模式下,需要填写sentinel_master_name:master_or_slave@sentinel_cluster_address。sentinel_master_name表示sentinel配置下master的名字,master_or_slave表示从sentinel中选择的db是master还是slave,sentinel_cluster_address表示sentinel的单节点或者集群地址,其中集群地址以分号(;)分割。例如:mymaster:master@127.0.0.1:26379;127.0.0.1:26380。注意,如果是sentinel模式,目前只能拉取一个master或者slave信息,如果需要拉取多个节点,需要启动多个shake。
cluster模式下,需要填写集群地址,以分号(;)分割。例如:10.1.1.1:20331;10.1.1.2:20441。同样也支持上面sentinel介绍的自动发现机制,包含@即可,参考3.2。
proxy模式下,需要填写单个proxy的地址,此模式目前仅用于rump。
source.password_raw:源redis的密码。
target.type: 目的redis的类型,与source.type一致。注意,目的端如果是阿里云的集群版,类型请填写proxy,填写cluster只会同步db0。
target.address:目的redis的地址。从1.6版本开始我们支持集群版,不同的类型对应不同的地址。 standalone模式,参见source.address。
sentinel模式,需要填写sentinel_master_name@sentinel_cluster_address。sentinel_master_name表示sentinel配置下master的名字,sentinel_cluster_address表示sentinel的单节点或者集群地址,其中集群地址以分号(;)分割。例如:mymaster@127.0.0.1:26379;127.0.0.1:26380
cluster模式,参见source.address。
proxy模式下,填写proxy的地址,如果是多个proxy,则round-robin循环负载均衡连接,保证一个源端db连接只会对应一个proxy。如果是阿里云的集群版请选择这种模式。
target.password_raw:目的redis的密码。
用户配置完配置文件,然后以不同的模式启动即可:./redis-shake -conf=redis-shake.conf -type=sync。

3.1 单个节点到单个节点配置举例。
source.type: standalone
source.address: 10.1.1.1:20441
source.password_raw: 12345
target.type: standalone
target.address: 10.1.1.1:20551
target.password_raw: 12345
3.2 集群版cluster到集群版cluster配置举例
source.type: cluster
source.address: 10.1.1.1:20441;10.1.1.1:20443;10.1.1.1:20445
source.password_raw: 12345
target.type: cluster
target.address: 10.1.1.1:20551;10.1.1.1:20553;10.1.1.1:20555
target.password_raw: 12345
  对于source.address或者target.address,需要配置源端的所有集群中db节点列表以及目的端集群所有db节点列表,用户也可以启用自动发现机制,地址以'@'开头,redis-shake将会根据cluster nodes命令自动去探测有几个节点。对于source.address,用户可以在'@'前面配置master(默认)或者slave表示分表从master或者slave进行拉取;对于target.address,只能是master或者不配置:

source.type: cluster
source.address: master@10.1.1.1:20441 # 将会自动探测到10.1.1.1:20441集群下的所有节点,并从所有master进行拉取。同理如果是slave@10.1.1.1:20441将会扫描集群下的所有slave节点。
source.password_raw: 12345
target.type: cluster
target.address: @10.1.1.1:20551 # 将会自动探测到10.1.1.1:20551集群下的所有节点,并写入所有master。
target.password_raw: 12345
  以上的说明是开源cluster,当然,源端也可以是别的集群架构模式,比如带proxy的集群(比如codis,或者别的云集群架构,但这种情况下有些不支持自动发现,需要手动配置所有master或者slave的地址),那么需要选择db节点进行拉取,source.type同样选择cluster,source.address后面跟所有db的地址(只要主或者从的其中一个即可)。

3.3 集群版cluster到proxy配置举例
source.type: cluster
source.address: 10.1.1.1:20441;10.1.1.1:20443;10.1.1.1:20445;10.1.1.1:20447
source.password_raw: 12345
target.type: proxy
target.address: 10.1.1.1:30331;10.1.1.1:30441;10.1.1.1:30551
target.password_raw: 12345
  source.address同样支持自动发现机制,参考3.2。此外,target.address为proxy的地址,proxy支持roundrobin写入,也就是说,对于这个配置来说,10.1.1.1:20441和10.1.1.1:20447将会写入10.1.1.1:30331;10.1.1.1:20443写入10.1.1.1:30441;10.1.1.1:20445写入10.1.1.1:30551。
  如3.2中所述,源端也可以是别的集群架构模式。

3.4 主从版/单节点到cluster配置举例
source.type: standalone
source.address: 10.1.1.1:20441
source.password_raw: 12345
target.type: cluster
target.address: 10.1.1.1:30331;10.1.1.1:30441;10.1.1.1:30551
target.password_raw: 12345


 

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凤舞飘伶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值