使用redis-shake工具进行redis的数据同步
前言:
工作中将常遇到测试环境和正式环境的数据同步或者需要进行数据迁移,对于mysql数据库的方案倒是不少,但是redis中如何快速便捷的迁移呢?答案是阿里云提供的:redis-shake
RedisShake是阿里云基于豌豆荚开源的redis-port进行二次开发的一个支持Redis异构集群实时同步的工具,它和redis-migrate-tool相比较,我觉得它的优点在于支持前缀key的同步,支持多DB同步,而redis-migrate-tool 只能全量同步,并且如果源做了分库,同步到目标Redis的时候都同步到了db0一个库里面了,这对于做了分库场景的业务是不可行的。
基本功能
redis-shake它支持解析、恢复、备份、同步四个功能
- 恢复restore:将RDB文件恢复到目的redis数据库。
- 备份dump:将源redis的全量数据通过RDB文件备份起来。
- 解析decode:对RDB文件进行读取,并以json格式解析存储。
- 同步sync:支持源redis和目的redis的数据同步,支持全量和增量数据的迁移,支持单节点、主从版、集群版之间的互相同步。
- 同步rump:支持源redis和目的redis的数据同步,仅支持全量的迁移,采用scan和restore命令进行迁移,支持不同云厂商不同redis版本的迁移。
参考官方文档: https://github.com/alibaba/RedisShake
基本原理
RedisShake同步原理
- 源Redis服务实例相当于主库,Redis-shake相当于从库,它会发送psync指令给源Redis服务实例。
- 源Redis实例先把RDB文件传输给 Redis-shake ,Redis-shake 会把RDB文件发送给目的实例。
- 源实例会再把增量命令发送给 Redis-shake ,Redis-shake负责把这些增量命令再同步给目的实例。
RedisShake执行过程
- 启动Redis-shake进程,这个进程模拟了一个 Redis 实例,Redis-shake的基本原理就是模拟一个Slave从节点加入源Redis集群,然后进行增量的拉取(通过psync命令)。
- Redis-shake进程和数据迁出的源实例进行数据的全量拉取同步,并回放,这个过程和 Redis 主从实例的全量同步是类似的
版本迭代特性
现在 redis-shake 有两个主版本:
redis-shake 2.x:持续更新 3 年,目前停止更新与答疑,遇到问题推荐尝试 3.x 版本
redis-shake 3.x:基于 redis-shake 2.x 重写,代码可读性高,性能较佳
部署和使用
1、部署安装redis-shake
1.1源端创建测试数据(也可以可视化工具自行添加一些测试数据)
[root@localhost ~]# for line in {1..10000};do /opt/redis6/bin/redis-cli -h 192.168.1.191 -p 7001 -c -a 1UEJjjGfYZU7dCWy set ops_${line} ${line}; done
2、下载解压redis-shake文件
[root@localhost ~]# mkdir redis-shake && cd redis-shake
[root@localhost redis-shake]# wget https://github.com/alibaba/RedisShake/releases/download/v3.1.7/redis-shake-linux-amd64.tar.gz
[root@localhost redis-shake]# tar axf redis-shake-linux-amd64.tar.gz
一、数据迁移实操
迁移环境信息如下:
主机IP | 操作系统 | Redis版本 | CPU架构 | 端口 | 角色 |
---|---|---|---|---|---|
192.168.1.191 | Centos7.6 | 6.2.8 | x86_64 | 7001 | 源主机 |
192.168.1.192 | Centos7.6 | 6.2.8 | x86_64 | 7001 | 目标主机 |
3、修改配置文件(sync.toml)
type = "sync" # 同步机制实现
[source] # 源Redis服务实例
version = 5.0 # 填写Redis源服务版本, 例如:2.8, 4.0, 5.0, 6.0, 6.2, 7.0, ...。
address = "127.0.0.1:6379" # 源Redis服务实例 地址+端口
username = "" # 如果Redis没有配置ACL,则可以不填写,否则需要填写用户名
password = "" # 如果Redis没有配置ACL,则可以不填写,否则需要填写密码
tls = false # 是否开启tls安全机制
elasticache_psync = "" # 是否支持AWS的elasticache
[target]
type = "standalone" # 选择Redis的类型:"standalone:单机模式" or "cluster:集群模式"
version = 5.0 # 填写Redis源服务版本, 例如:2.8, 4.0, 5.0, 6.0, 6.2, 7.0, ...。
# 如果目标Redis服务实例属于cluster集群模式, 那么可以写入其中一个节点的地址和端口.
# redis-shake 会通过`cluster nodes` 命令获取其他的节点地址和端口
address = "127.0.0.1:6380" # 填写的对应的ip加端口
username = "" # 如果Redis没有配置ACL,则可以不填写,否则需要填写用户名
password = "" # 如果Redis没有配置ACL,则可以不填写,否则需要填写密码
tls = false # 是否开启tls安全机制
[advanced]
dir = "data" # 数据同步的存储目录
# 设置使用的最大CPU核心数, 如果设置了0 代表着 使用 runtime.NumCPU() 实际的cpu cores数量
ncpu = 4
# 开启pprof性能检测的port, 0代表着禁用
pprof_port = 0
# 开启metric port端口, 0代表着禁用
metrics_port = 0
# log的相关设置
log_file = "redis-shake.log" # 设置对应的日志文件名称
log_level = "info" # debug, info or warn # 设置对应的日志级别
log_interval = 5 # in seconds # 日志打印频次
# redis-shake gets key and value from rdb file, and uses RESTORE command to
# create the key in target redis. Redis RESTORE will return a "Target key name
# is busy" error when key already exists. You can use this configuration item
# to change the default behavior of restore:
# panic: redis-shake will stop when meet "Target key name is busy" error.
# rewrite: redis-shake will replace the key with new value.
# ignore: redis-shake will skip restore the key when meet "Target key name is busy" error.
rdb_restore_command_behavior = "rewrite" # restore的操作类型:panic, rewrite or skip
# pipeline的大小数量阈值
pipeline_count_limit = 1024
# Client query buffers accumulate new commands. They are limited to a fixed
# amount by default. This amount is normally 1gb.
target_redis_client_max_querybuf_len = 1024_000_000
# In the Redis protocol, bulk requests, that are, elements representing single
# strings, are normally limited to 512 mb.
target_redis_proto_max_bulk_len = 512_000_000
同步日志注释
当打印的日志出现send RDB finished,表示完成全量数据迁移,接下来进入增量数据迁移阶段。
日志信息中各参数说明如下:
- allowOps:表示每秒向目标库发送多少条命令。 说明 通常当allowOps为0时,表示数据迁移完成,可以停止redis-shake。但源库会定时发送PING命令,所以allowOps偶尔不为0。
- disallowOps:表示每秒过滤的命令数。
- entryId:从1开始计数,表示redis-shake共处理多少条命令。
- InQueueEntriesCount:表示还剩余多少条命令待发送。 暂停向源库写入数据,等待返回日志中allowOps对应值连续多次为0时,使用Ctrl+C组合键停止运行redis-shake。 此时目标库的数据与源库完全一致,您可以将业务的数据库服务由自建Redis数据库切换至Tair或Redis实例。
toml文件的编写方式
单机 -> 单机 配置格式:
type = "sync"
[source]
address = "127.0.0.1:6379"
password = "123456" ## 根据自身密码填写
[target]
type = "standalone" #这里type属性设置一定为standalone
address = "127.0.0.1:6379"
password = "123456" ## 根据自身密码填写
启动 redis-shake:
root@ubuntu20-171:RedisShake# ./redis-shake sync.toml
单机->集群 配置格式:
type = "sync"
[source] #数据源配置
address = "127.0.0.1:6379"
password = "1234566"
[target] #目的源配置
type = "cluster" #这里type属性设置一定为cluster
address = "127.0.0.1:6379" # 这里写集群中的任意一个节点的地址即可
password = "1234566"
启动 redis-shake:
root@ubuntu20-171:RedisShake# ./redis-shake sync.toml
集群 -> 集群 配置格式:
在 RedisShake V3系列版本,也就是当前使用最多的版本中,集群与集群之间的redis数据同步,统一在单机到集群的基础上,为每一个数据源端的redis master节点建立一个shake进程
⚠️注意:不要在同一个目录启动多个 redis-shake,因为 redis-shake 会在本地存储临时文件,多个 redis-shake 之间的临时文件会干扰,正确做法是建立多个目录。
配置
修改 sync.toml:
type = "sync"
[source]
address = "192.168.0.1:6379" # 集群 C 中任意一个节点地址
password = "r-ccccc:xxxxx"
[target]
type = "cluster"
address = "192.168.1.1:6380" # 集群 D 中任意一个节点地址
password = "r-ddddd:xxxxx"
启动 redis-shake:
root@ubuntu20-171:RedisShake# cd bin/cluster_helper
root@ubuntu20-171:cluster_helper# python3 cluster_helper.py ../redis-shake ../sync.toml
参数 1 是 redis-shake 可执行程序的路径
参数 2 是配置文件路径
2.3、redis-shake执行数据迁移
1、执行下述命令启动redis-shake,redis-shake将开始执行数据迁移
[root@localhost redis-shake]# ./redis-shake sync.toml
日志信息
同步分为三个阶段:
等待源端save rdb完毕,日志如下:
[root@localhost redis-shake]# ./redis-shake sync.toml
2024-11-20 23:16:45 INF GOOS: linux, GOARCH: amd64
2024-11-20 23:16:45 INF Ncpu: 4, GOMAXPROCS: 4
2024-11-20 23:16:45 INF pid: 5493
2024-11-20 23:16:45 INF pprof_port: 0
2024-11-20 23:16:45 INF No lua file specified, will not filter any cmd.
2024-11-20 23:16:45 INF no password. address=[192.168.48.131:6319]
2024-11-20 23:16:45 INF redisClusterWriter load cluster nodes. line=b4cc6c47038acb2a5f1198f23473d757d0991b24 127.0.0.1:6319@16319 myself,master - 0 1732173403000 1 connected 0-5460
2024-11-20 23:16:45 INF no password. address=[127.0.0.1:6319]
2024-11-20 23:16:45 INF redisWriter connected to redis successful. address=[127.0.0.1:6319]
2024-11-20 23:16:45 INF redisClusterWriter load cluster nodes. line=61cdca1ce4f7524e7278081474168361f2036d39 127.0.0.1:6339@16339 master - 0 1732173404370 3 connected 10923-16383
2024-11-20 23:16:45 INF no password. address=[127.0.0.1:6339]
2024-11-20 23:16:45 INF redisWriter connected to redis successful. address=[127.0.0.1:6339]
2024-11-20 23:16:45 INF redisClusterWriter load cluster nodes. line=ef3dd5f8304d590eb730b7a9900a7d5a2ccbab02 127.0.0.1:6329@16329 master - 0 1732173401171 2 connected 5461-10922
2024-11-20 23:16:45 INF no password. address=[127.0.0.1:6329]
2024-11-20 23:16:45 INF redisWriter connected to redis successful. address=[127.0.0.1:6329]
2024-11-20 23:16:45 INF redisClusterWriter connected to redis cluster successful. addresses=[127.0.0.1:6319 127.0.0.1:6339 127.0.0.1:6329]
2024-11-20 23:16:45 INF no password. address=[192.168.0.111:7005]
2024-11-20 23:16:45 INF psyncReader connected to redis successful. address=[192.168.0.111:7005]
2024-11-20 23:16:45 WRN remove file. filename=[133117.aof]
2024-11-20 23:16:45 WRN remove file. filename=[dump.rdb]
2024-11-20 23:16:45 INF start save RDB. address=[192.168.0.111:7005]
2024-11-20 23:16:45 INF send [replconf listening-port 10007]
2024-11-20 23:16:45 INF send [PSYNC ? -1]
2024-11-20 23:16:45 INF receive [FULLRESYNC 2f2ce402b674be6aa3df35b1a9a27073bd67275c 135609]
2024-11-20 23:16:45 INF source db is doing bgsave. address=[192.168.0.111:7005]
2024-11-20 23:16:45 INF source db bgsave finished. timeUsed=[0.12]s, address=[192.168.0.111:7005]
2024-11-20 23:16:45 INF received rdb length. length=[1094]
2024-11-20 23:16:45 INF create dump.rdb file. filename_path=[dump.rdb]
2024-11-20 23:16:45 INF save RDB finished. address=[192.168.0.111:7005], total_bytes=[1094]
全量同步阶段
数据量大的话会显示进度百分比:(我dev环境数据量小)日志如下:
2024-11-20 23:16:45 INF start send RDB. address=[192.168.0.111:7005]
2024-11-20 23:16:45 INF RDB version: 7
2024-11-20 23:16:45 INF RDB AUX fields. key=[redis-ver], value=[3.2.100]
2024-11-20 23:16:45 INF RDB AUX fields. key=[redis-bits], value=[64]
2024-11-20 23:16:45 INF RDB AUX fields. key=[ctime], value=[1732173405]
2024-11-20 23:16:45 INF RDB AUX fields. key=[used-mem], value=[0]
2024-11-20 23:16:45 INF RDB resize db. db_size=[10], expire_size=[0]
2024-11-20 23:16:45 INF send RDB finished. address=[192.168.0.111:7005], repl-stream-db=[0]
2024-11-20 23:16:45 INF start save AOF. address=[192.168.0.111:7005]
2024-11-20 23:16:45 INF AOFWriter open file. filename=[135609.aof]
2024-11-20 23:16:46 INF AOFReader open file. aof_filename=[135609.aof]
## 全量完成
## 显示 send RDB finished 说明全量完成
增量同步
日志如下:
2024-11-20 23:16:50 INF syncing aof. allowOps=[2.00], disallowOps=[0.00], entryId=[9], InQueueEntriesCount=[0], unansweredBytesCount=[0]bytes, diff=[0], aofReceivedOffset=[0], aofAppliedOffset=[0]
2024-11-20 23:16:55 INF syncing aof. allowOps=[0.20], disallowOps=[0.00], entryId=[10], InQueueEntriesCount=[0], unansweredBytesCount=[0]bytes, diff=[0], aofReceivedOffset=[135623], aofAppliedOffset=[135623]
2024-11-20 23:17:00 INF syncing aof. allowOps=[0.00], disallowOps=[0.00], entryId=[10], InQueueEntriesCount=[0], unansweredBytesCount=[0]bytes, diff=[0], aofReceivedOffset=[135623], aofAppliedOffset=[135623]
2024-11-20 23:17:05 INF syncing aof. allowOps=[0.20], disallowOps=[0.00], entryId=[11], InQueueEntriesCount=[0], unansweredBytesCount=[0]bytes, diff=[0], aofReceivedOffset=[135637], aofAppliedOffset=[135637]
2024-11-20 23:17:10 INF syncing aof. allowOps=[0.00], disallowOps=[0.00], entryId=[11], InQueueEntriesCount=[0], unansweredBytesCount=[0]bytes, diff=[0], aofReceivedOffset=[135637], aofAppliedOffset=[135637]
2024-11-20 23:17:15 INF syncing aof. allowOps=[0.20], disallowOps=[0.00], entryId=[12], InQueueEntriesCount=[0], unansweredBytesCount=[0]bytes, diff=[0], aofReceivedOffset=[135651], aofAppliedOffset=[135651]
出现字样syncing aof. allowOps=[0.00] 或者出现syncing aof. allowOps=[0.00]的频率很高 说明 当前增量同步完成 可以ctrl+c停止redis-shake 进程,但是 一般不会为0 或者 因为有client在一直连接
查看源端和目标端的情况
源端
127.0.0.1:6379> info Replication
# Replication
role:master
connected_slaves:1 ### 源端会启动一个redis-shake的从节点
slave0:ip=127.0.0.1,port=10007,state=online,offset=1040,lag=0 ###shake进程
master_failover_state:no-failover
master_replid:b20dfd6f617c867cfaf0910c08be627fe7b922d7
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1040
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:104857600
repl_backlog_first_byte_offset:229
repl_backlog_histlen:812
127.0.0.1:6379> info Keyspace ## 查看比对key数量
# Keyspace
db0:keys=6,expires=0,avg_ttl=0
db2:keys=3,expires=0,avg_ttl=0
db10:keys=3,expires=0,avg_ttl=0
目标端
127.0.0.1:7000> info Replication
# Replication
role:master ### 目标端 还是master角色
connected_slaves:0
master_failover_state:no-failover
master_replid:1017a92f7fd8447d304172796ea6044a1f801e26
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:104857600
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:7000> info Keyspace ## 对比key数量
# Keyspace
db0:keys=6,expires=0,avg_ttl=0
db2:keys=3,expires=0,avg_ttl=0
db10:keys=3,expires=0,avg_ttl=0
注意事项
如果目标库的数据淘汰策略(maxmemory-policy)配置为noeviction以外的值,可能导致目标库的数据与源库不一致
如果源库中的某些Key使用了过期(expire)机制,由于可能存在Key已过期但未被及时删除的情形,所以在目标库中查看(如通过info命令)到的Key数量会比源库的Key数量少
以上就是这期全部内容,感谢观看