目录
一、问题提出
随着业务量的增长,主从复制的 Redis 架构中,单实例的 OPS(Operations Per Second,每秒操作数) 已经接近峰值,很自然想到了数据水平分片。Redis 3.0 版的一大特性就是支持数据分片的集群功能。集群的特点在于拥有和单机实例同样的性能,同时在网络分区后能够提供一定的可访问性以及对主库故障恢复的支持。哨兵与集群是两个独立的功能,但从特性来看哨兵可以视为集群的子集,当不需要数据分片或者在客户端进行分片的场景下使用哨兵就足够了,但如果需要进行水平扩容,则集群是一个较好的选择。
当一个全新的 Redis 集群已经搭建完成后,需要将原来单实例 Redis 中的数据在线迁移到 Redis 集群中,并且在迁移过程不能影响现有服务,具体需求如下:
- 在线数据迁移,不能停库。
- 切换服务中断时间最小化,即 0 或接近 0。
- 业务不受影响,即没有性能缺失。
- 保证迁移数据的完整性。
- 应用改动量极小。
为了满足以上需求,经过一番调研,发现 Redis-Migrate-Tool 工具能胜任此数据迁移工作。
二、关于 Redis-Migrate-ool
Redis-Migrate-Tool(RMT),是唯品会开源的 Redis 数据迁移工具,主要用于异构 Redis 集群间的数据在线迁移,即数据迁移过程中源集群仍可以正常接受业务读写请求,无业务中断服务时间。目前该项目已经开源在GitHub上(GitHub - vipshop/redis-migrate-tool: A convenient and useful tool for migrating data between redis group.)。
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 文件恢复到目标库。
三、实验
建立一个六实例 Redis 集群和一个单实例 Redis,向单实例 Redis 插入测试数据,然后用 Redis-Migrate-Tool 将数据迁移至集群中。注意 Redis-Migrate-Tool目前不支持 Redis 4.x,因此本实验使用 Redis 3.2.3 版本。
1. 建立 Redis 集群
集群由三节点六实例组成,IP 和端口如下:
10.10.10.1:20001、20002
10.10.10.2:20001、20002
10.10.10.3:20001、20002
(1)准备配置文件
redis.conf 配置文件类似下面的内容,每个实例只有端口不一样:
rename-command flushAll ""
daemonize yes
port 20001
dir "/20001"
pidfile "/20001/redis.pid"
logfile "/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)启动所有集群实例
在三个节点上分别执行以下命令,共创建六个单实例。
~/redis-3.2.3/src/redis-server /20001/redis.conf
~/redis-3.2.3/src/redis-server /20001/redis.conf
启动后,可以使用 Redis 命令行客户端连接任意一个节点使用 info 命令来判断集群是否正常启用了:
# ~/redis-3.2.3/src/redis-cli -p 20001 -a 123456 info cluster
# Cluster
cluster_enabled:1
#
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 10.10.10.1:20001 10.10.10.1:20002 10.10.10.2:20001 10.10.10.2:20002 10.10.10.3:20001 10.10.10.3:20002
其中 create 参数表示要初始化集群,--replicas 1 表示每个主库拥有的从库个数为 1,所以整个集群共有三个主库以及三个从库,而主从的节点分配由 redis-trib.rb 来控制。
2. 建立单实例 Redis
(1)准备配置文件
redis.conf 配置文件内容如下,之比集群配置文件少了一行 cluster-enabled yes。
rename-command flushAll ""
daemonize yes
port 20001
dir "/20001"
pidfile "/20001/redis.pid"
logfile "/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 /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.conf 配置文件内容如下:
[source]
type: single
servers :
-10.10.10.4:20001
redis_auth: 123456
[target]
type: redis cluster
servers:
-10.10.10.1: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:
# 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)
#
(3)查看集群数据
# ~/redis-3.2.3/src/redis-cli -p 20001 -a 123456 get foo
(error) MOVED 12182 172.16.1.125:20001
# ~/redis-3.2.3/src/redis-cli -h 172.16.1.125 -p 20001 -a 123456 get foo
"bar"
# ~/redis-3.2.3/src/redis-cli -p 20001 -a 123456 get foo1
(error) MOVED 13431 172.16.1.125:20001
# ~/redis-3.2.3/src/redis-cli -h 172.16.1.125 -p 20001 -a 123456 get foo1
"bar1"
# ~/redis-3.2.3/src/redis-cli -p 20001 -a 123456 get key1
(error) MOVED 9189 172.16.1.126:20001
# ~/redis-3.2.3/src/redis-cli -h 172.16.1.126 -p 20001 -a 123456 get key1
"123"
#
可以看到,三个测试 key 都复制到了集群。以后在单实例上的任何数据变动都会复制到集群中。
(4)观察迁移状态
使用 redis-cli 可以连接 rmt.conf 中配置的端口,执行 info 命令,就可以观察迁移的状态。total_msgs_outqueue 可以判断是否有 oplog 在队列中等待处理,如果 total_msgs_outqueue>0,继续等待。
# ~/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:/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
#
(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。