redis-migrate-tool在线进行数据迁移

呼延永新
2023-12-01

一、问题提出

        随着业务量的增长,主从复制的redis架构中,单实例的OPS已经接近峰值,很自然想到了数据水平分片。redis 3.0版的一大特性就是支持数据分片的集群功能。集群的特点在于拥有和单机实例同样的性能,同时在网络分区后能够提供一定的可访问性以及对主库故障恢复的支持。哨兵与集群是两个独立的功能,但从特性来看哨兵可以视为集群的子集,当不需要数据分片或者在客户端进行分片的场景下使用哨兵就足够了,但如果需要进行水平扩容,则集群是一个较好的选择。

        当一个全新的redis集群已经搭建完成后,需要将原来单实例redis中的数据在线迁移到redis集群中,并且在迁移过程不能影响现有服务,具体需求如下:

在线数据迁移,不能停库。
切换服务中断时间最小化,即0或接近于0。
业务不受影响,即没有性能缺失。
保证迁移数据的完整性。
应用改动量极小。 
        为了满足以上需求,经过一番调研,发现redis-migrate-tool工具能胜任此数据迁移工作。

二、关于redis-migrate-tool

        Redis-Migrate-Tool(RMT),是唯品会开源的redis数据迁移工具,主要用于异构redis集群间的数据在线迁移,即数据迁移过程中源集群仍可以正常接受业务读写请求,无业务中断服务时间。目前该项目已经开源在GitHub上(https://github.com/vipshop/redis-migrate-tool)。

        RMT具有以下特性:

快速。
多线程。
基于redis复制。
实时迁移。
迁移过程中,源集群不影响对外提供服务。
异构迁移。
支持Twemproxy集群,redis cluster集群,rdb文件 和 aof文件。
过滤功能。
当目标集群是Twemproxy,数据会跳过Twemproxy直接导入到后端的redis。
迁移状态显示。
完善的数据抽样校验。
        RMT支持异构redis相互之间的迁移,数据来源可以是:单独的redis实例,twemproxy集群,redis cluster,rdb文件,aof文件。目标可以是:单独的redis实例,twemproxy集群,redis cluster,rdb文件。

        RMT启动后模拟成redis slave,请求master的全量数据和增量数据。RMT收到数据之后解析成redis协议格式的oplog(写操作),然后发送给目标库。有两种请求方式,source_safe: true,对于同一ip上的redis,逐个的请求全量数据(RDB);source_safe: false,并行请求同步全量数据。source_safe: false时,需要注意多个源redis所在的同一主机是否有足够的内存和RDB并发落盘时的IOPS性能。

        RMT可以帮你从备份的AOF和RDB文件恢复到目标库。

三、实验

        我们建立一个6实例redis集群和一个单实例redis,向单实例redis插入测试数据,然后用redis-migrate-tool将数据迁移至集群中。注意redis-migrate-tool目前不支持redis 4.x,因此本实验使用redis 3.2.3版本。

1. 建议集群redis

        集群由3节点6实例组成,IP和端口如下:
172.16.1.127:20001、20002
172.16.1.126:20001、20002
172.16.1.125:20001、20002(1)准备配置文件
        redis.conf配置文件类似下面的内容,每个实例只有端口不一样:

rename-command flushAll ""
daemonize yes
port 20001
dir "/var/redis/20001"
pidfile "/var/redis/20001/redis.pid"
logfile "/var/redis/20001/redis.log"
dbfilename "dump.rdb"
save 900 1
appendonly no
appendfilename "appendonly.aof"
appendfsync always
maxmemory 1gb
maxmemory-policy volatile-lru
maxmemory-samples 3
slowlog-log-slower-than 10000
repl-backlog-size 64mb
timeout 0
repl-timeout 240
cluster-enabled yes
 
requirepass "123456"
masterauth "123456"
protected-mode no
 
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 2gb 1gb 300
(2)启动所有集群实例
        在三个节点上分别执行以下命令,共创建6个单实例。

~/redis-3.2.3/src/redis-server /var/redis/20001/redis.conf
~/redis-3.2.3/src/redis-server /var/redis/20001/redis.conf
        启动后,可以使用redis命令行客户端连接任意一个节点使用info命令来判断集群是否正常启用了:

[root@hdp4/var/redis]#~/redis-3.2.3/src/redis-cli -p 20001 -a 123456 info cluster
# Cluster
cluster_enabled:1
[root@hdp4/var/redis]#
        cluster_enabled为1表示集群正常启用了。现在每个节点都是完全独立的,下面将它们加入同一个集群。

(3)执行redis-trib.rb脚本创建集群
        这步需要注意一点,我们的redis设置了密码,因此需要修改redis-trib.rb源码,在“@r = Redis.new”行将密码加入(集群中的所有redis使用同一密码):

@r = Redis.new(:host => @info[:host], :port => @info[:port], :timeout => 60, :password => "123456")
        然后执行下面的命令创建集群:

~/redis-3.2.3/src/redis-trib.rb create --replicas 1 172.16.1.127:20001 172.16.1.127:20002 172.16.1.126:20001 172.16.1.126:20002 172.16.1.125:20001 172.16.1.125:20002
        其中create参数表示要初始化集群,--replicas 1表示每个主库拥有的从库个数为1,所以整个集群共有3个主库以及3个从库,而主从的节点分配由redis-trib.rb来控制。

2. 建立单实例redis

(1)准备配置文件

redis.conf配置文件内容如下,之比集群配置文件少了一行cluster-enabled yes:
rename-command flushAll ""
daemonize yes
port 20001
dir "/var/redis/20001"
pidfile "/var/redis/20001/redis.pid"
logfile "/var/redis/20001/redis.log"
dbfilename "dump.rdb"
save 900 1
appendonly no
appendfilename "appendonly.aof"
appendfsync always
maxmemory 1gb
maxmemory-policy volatile-lru
maxmemory-samples 3
slowlog-log-slower-than 10000
repl-backlog-size 64mb
timeout 0
repl-timeout 240
 
requirepass "123456"
masterauth "123456"
protected-mode no
 
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 2gb 1gb 300
(2)启动单实例

~/redis-3.2.3/src/redis-server /var/redis/20001/redis.conf
3. 源码安装redis-migrate-tool

unzip redis-migrate-tool-master.zip
cd redis-migrate-tool-master
autoreconf -fvi
./configure
make
4. 配置redis-migrate-tool

        rmt.con配置文件内容如下:

[source]
type: single
servers : 
-172.16.1.124:20001
redis_auth: 123456
 
[target]
type: redis cluster
servers:
-172.16.1.127:20001
redis_auth: 123456
 
[common]
listen: 0.0.0.0:8888
        其中目标为集群中任意节点即可。

5. 测试

(1)在单实例中生成一些测试key

~/redis-3.2.3/src/redis-cli -p 20001 -a 123456 set foo bar
~/redis-3.2.3/src/redis-cli -p 20001 -a 123456 set foo1 bar1
~/redis-3.2.3/src/redis-cli -p 20001 -a 123456 set key1 123
(2)启动redis-migrate-tool

~/redis-migrate-tool-master/src/redis-migrate-tool -c rmt.conf -o log -d
        redis-migrate-tool的监听端口为8888:

[root@hdp1~/redis-migrate-tool-master]#lsof -i:8888
COMMAND      PID USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
redis-mig 126471 root    9u  IPv4 137161329      0t0  TCP *:ddi-tcp-1 (LISTEN)
[root@hdp1~/redis-migrate-tool-master]#
(3)查看集群数据

[root@hdp4/var/redis]#~/redis-3.2.3/src/redis-cli -p 20001 -a 123456 get foo
(error) MOVED 12182 172.16.1.125:20001
[root@hdp4/var/redis]#~/redis-3.2.3/src/redis-cli -h 172.16.1.125 -p 20001 -a 123456 get foo
"bar"
[root@hdp4/var/redis]#~/redis-3.2.3/src/redis-cli -p 20001 -a 123456 get foo1
(error) MOVED 13431 172.16.1.125:20001
[root@hdp4/var/redis]#~/redis-3.2.3/src/redis-cli -h 172.16.1.125 -p 20001 -a 123456 get foo1
"bar1"
[root@hdp4/var/redis]#~/redis-3.2.3/src/redis-cli -p 20001 -a 123456 get key1
(error) MOVED 9189 172.16.1.126:20001
[root@hdp4/var/redis]#~/redis-3.2.3/src/redis-cli -h 172.16.1.126 -p 20001 -a 123456 get key1
"123"
[root@hdp4/var/redis]#
        可以看到,3个测试key都复制到了集群。以后在单实例上的任何数据变动都会复制到集群中。(4)观察迁移状态
        使用redis-cli可以连接 rmt.conf 中配置的端口,执行info命令,就可以观察迁移的状态。total_msgs_outqueue可以判断是否有oplog在队列中等待处理,如果total_msgs_outqueue>0,请继续等待。

[root@hdp1~/redis-migrate-tool-master]#~/redis-3.2.3/src/redis-cli -p 8888 info
# Server
version:0.1.0
os:Linux 3.10.0-327.el7.x86_64 x86_64
multiplexing_api:epoll
gcc_version:4.8.5
process_id:126471
tcp_port:8888
uptime_in_seconds:141
uptime_in_days:0
config_file:/root/redis-migrate-tool-master/rmt.conf
 
# Clients
connected_clients:1
max_clients_limit:100
total_connections_received:2
 
# Memory
mem_allocator:jemalloc-4.0.4
 
# Group
source_nodes_count:1
target_nodes_count:3
 
# Stats
all_rdb_received:1
all_rdb_parsed:1
all_aof_loaded:0
rdb_received_count:1
rdb_parsed_count:1
aof_loaded_count:0
total_msgs_recv:3
total_msgs_sent:3
total_net_input_bytes:305
total_net_output_bytes:96
total_net_input_bytes_human:305B
total_net_output_bytes_human:96B
total_mbufs_inqueue:0
total_msgs_outqueue:0
[root@hdp1~/redis-migrate-tool-master]#
(5)检验集群和目标的差异

[root@hdp1~/redis-migrate-tool-master]#src/redis-migrate-tool -c rmt.conf -o log -C "redis_check 10000"
Check job is running...
 
Checked keys: 10000
Inconsistent value keys: 0
Inconsistent expire keys : 0
Other check error keys: 0
Checked OK keys: 10000
 
All keys checked OK!
Check job finished, used 0.136s
[root@hdp1~/redis-migrate-tool-master]#
四、业务切换

        如果是异构集群的迁移,更改redis驱动/客户端和修改代码,重新发布是必然的事情。如果是同构集群,做配置发布就可以。如果没有做重启发布,等待源集群没有连接之后,可以关闭REDIS-MIGRATE-TOOL进程(kill或在redis-cli shutdown)。或者重启应用,强制断开长连接。 

五、注意事项

REDIS-MIGRATE-TOOL迁移数据到twemproxy,需要保持rmt.conf 中 [target] hash、distribution、servers 三个参数和目标集群的twemproxy配置严格一致。
迁移中和业务切换之前,请反复观察"-o redis-migrate-tool.log"日志信息,确认是否有异常。
业务切换之前,请充分检查,特别是数据的一致性。
REDIS-MIGRATE-TOOL 建议部署在单独空闲机器上,同目的集群在同一个网段(跨机房迁移数据,可以提高迁移速度)。千万不要部署在源集群所在的机器,防止资源不足,比如内存,带宽,IOPS。
注意RDB传输是否超时。
redis client buf中的slave项,设置足够大的buffer size和超时时间。
运行此工具前,请确认redis源机器是否有足够的内存允许至少生成一个RDB文件。如果redis源机器有足够的内存允许所有的redis同时生成RDB文件,可以在配置文件rmt.conf中的添加配置项source_safe: false
以下命令不支持传播到目标redis组,因为keys使用这些命令可能会跨越不同的目标redis节点:RENAME、RENAMENX、RPOPLPUSH、BRPOPLPUSH、FLUSHALL、FLUSHDB、BITOP、MOVE、GEORADIUS、GEORADIUSBYMEMBER、EVAL、EVALSHA、SCRIPT、PFMERGE。

 类似资料: