Redis数据迁移与校验

  1. 云栖社区>
  2. 袋鼠云技术团队>
  3. 博客>
  4. 正文

Redis数据迁移与校验

梓杰 2019-08-08 21:59:17 浏览1200
展开阅读全文

  Redis常见的数据迁移方式有以下三种:RDB迁移、AOF迁移、主从复制。
  使用RDB和AOF文件可以实现跨网络隔离的数据迁移,但是无法做到增量迁移。主从复制可以实现增量迁移但是无法跨网络环境。另外,在过去对于数据迁移后的校验缺乏良好的手段。
  本文对一些开源工具进行调研与实践,结果如下:

一、Redis数据迁移

1.1 Redis数据迁移工具

1.1.1 redis-migration

  redis-migrate是一款轻量级的redis数据迁移工具,保持redis源码文件不做改动的前提下,只新增加了一个"redis-migration.c"文件。
  redis-migrate通过伪装成从库,获取数据文件并进行解析处理。redis-migration在迁移过程中对数据做了落地处理,工具本身没有对数据进行加载,内存开销很小。另外在迁移RDB文件时采用RTT这样一个时间窗口,在这个时间窗口里并发多个客户端就可以避免系统等待,极大提高性能;另外,AOF迁移时候使用了pipeline特性,批量发送,减少RTT来加速迁移。

1.1.2 redis-migrate-tool

  Redis-Migrate-Tool(RMT),是唯品会开源的redis数据迁移工具,主要用于异构redis集群间的数据在线迁移,数据迁移过程中源集群仍可以正常接受业务读写请求,无业务中断服务时间。
  RMT启动后模拟成redis从库,请求主库的全量数据和增量数据。RMT收到数据之后解析成redis协议格式的写操作,然后发送给目标库。

1.1.3 redis-port

  redis-port是一组开源工具集合,主要用于Redis节点间的数据库同步、数据导入、数据导出,支持 Redis的跨版本数据迁移。

1.1.4 redis-shake

  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-shake这款工具。

1.2 redis-shake介绍

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

./redis-shake -type=dump  -conf=redis-shake.conf #备份
./redis-shake -type=decode  -conf=redis-shake.conf #解析
[root@xsj redis-shake]# cat dump.rdb.0 
{"db":0,"type":"string","expireat":0,"key":"name","key64":"bmFtZQ==","value64":"emo="}
{"db":0,"type":"string","expireat":0,"key":"sj","key64":"c2o=","value64":"Mg=="}
{"db":0,"type":"string","expireat":0,"key":"a","key64":"YQ==","value64":"MQ=="}
{"db":0,"type":"string","expireat":0,"key":"zj","key64":"emo=","value64":"Mw=="}
{"db":0,"type":"string","expireat":0,"key":"b","key64":"Yg==","value64":"Mg=="}
./redis-shake -type=restore  -conf=redis-shake.conf #恢复
#全量/增量
127.0.0.1:6389> keys *
(empty list or set)
[root@xsj redis-shake]# ./redis-shake -type=rump  -conf=redis-shake.conf
127.0.0.1:6389> keys *
1) "zj"
2) "sj"
3) "b"
4) "name"
5) "a"
127.0.0.1:6389> flushdb
127.0.0.1:6389> keys *
(empty list or set)
[root@xsj redis-shake]# ./redis-shake -type=sync  -conf=redis-shake.conf
127.0.0.1:6389> keys *
1) "zj"
2) "sj"
3) "b"
4) "name"
5) "a"
#源端新增数据
127.0.0.1:6379> set test asd
OK
127.0.0.1:6389> get test
"asd"
2019/08/08 21:53:11 [INFO] dbSyncer[0] sync rdb done
2019/08/08 21:53:11 [INFO] input password is empty, skip auth address[127.0.0.1:6389] with type[auth].
2019/08/08 21:53:11 [WARN] dbSyncer[0] GetFakeSlaveOffset not enable when psync == false
2019/08/08 21:53:11 [INFO] dbSyncer[0] Event:IncrSyncStart      Id:redis-shake
2019/08/08 21:53:12 [INFO] dbSyncer[0] sync:  +forwardCommands=0      +filterCommands=0      +writeBytes=0
#由日志可以看到,sync会先完成全量,再进行增量。

  可以通过curl 127.0.0.1:9320/metric进行实时监控。
_

  由上文验证可以得知,redis-shake支持全量+增量的数据迁移,并且支持对RDB文件的备份以及解析。

二、Redis数据校验

2.1 redis-full-check

  redis-full-check是阿里云Redis&MongoDB团队开源的用于校验2个redis数据是否一致的工具。
  redis-full-check通过全量对比源端和目的端的redis中的数据的方式来进行数据校验,其比较方式通过多轮次比较:每次都会抓取源和目的端的数据进行差异化比较,记录不一致的数据进入下轮对比(记录在sqlite3 db中)。然后通过多轮比较不断收敛,减少因数据增量同步导致的源库和目的库的数据不一致。最后sqlite中存在的数据就是最终的差异结果。
_
  redis-full-check对比的方向是单向:抓取源库A的数据,然后检测是否位于B中,反向不会检测,也就是说,它检测的是源库是否是目的库的子集。如果希望对比双向,则需要对比2次,第一次以A为源库,B为目的库,第二次以B为源库,A为目的库。

127.0.0.1:6379> keys *
1) "name"
2) "zj"
3) "sj"
127.0.0.1:6379> get name
"zj"
127.0.0.1:6379> get zj
"3"
127.0.0.1:6379> get sj
"2"
[root@xsj bin]# ./redis-cli  -p 6389
127.0.0.1:6389> get name
"zj"
127.0.0.1:6389> get zj
"3"
127.0.0.1:6389> get sj
"2"
127.0.0.1:6389> keys *
1) "sj"
2) "name"
3) "zj"
[root@xsj redis-full-check]# ./redis-full-check --source 127.0.0.1:6379 --target 127.0.0.1:6389
INFO 2019-08-08-15:35:06](main.go:54): init log success
INFO 2019-08-08-15:35:06](full_check.go:238): sourceDbType=0, p.sourcePhysicalDBList=[meaningless]
INFO 2019-08-08-15:35:06](full_check.go:243): db=0:keys=3
INFO 2019-08-08-15:35:06](full_check.go:253): ---------------- start 1th time compare
INFO 2019-08-08-15:35:06](full_check.go:278): start compare db 0
INFO 2019-08-08-15:35:07](full_check.go:203): stat:
times:1, db:0, dbkeys:3, finish:33%, finished:true
KeyScan:{3 3 0}

INFO 2019-08-08-15:35:07](full_check.go:250): wait 5 seconds before start
INFO 2019-08-08-15:35:12](full_check.go:253): ---------------- start 2th time compare
INFO 2019-08-08-15:35:12](full_check.go:278): start compare db 0
INFO 2019-08-08-15:35:12](full_check.go:203): stat:
times:2, db:0, finished:true
KeyScan:{0 0 0}

INFO 2019-08-08-15:35:12](full_check.go:250): wait 5 seconds before start
INFO 2019-08-08-15:35:17](full_check.go:253): ---------------- start 3th time compare
INFO 2019-08-08-15:35:17](full_check.go:278): start compare db 0
INFO 2019-08-08-15:35:17](full_check.go:203): stat:
times:3, db:0, finished:true
KeyScan:{0 0 0}

INFO 2019-08-08-15:35:17](full_check.go:323): --------------- finished! ----------------
all finish successfully, totally 0 key(s) and 0 field(s) conflict
[root@xsj redis-full-check]# ll
total 12096
-rw-r--r-- 1 1320024 users     1843 Jul 23 20:12 ChangeLog
-rwxr-xr-x 1 1320024 users 12318467 Jul 23 20:12 redis-full-check
-rw-r--r-- 1 root    root     20480 Aug  8 15:35 result.db.1
-rw-r--r-- 1 root    root     20480 Aug  8 15:35 result.db.2
-rw-r--r-- 1 root    root     20480 Aug  8 15:35 result.db.3
[root@xsj redis-full-check]# sqlite3 result.db.3
SQLite version 3.7.17 2013-05-20 00:56:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> select * from key;
127.0.0.1:6389> set name diff
OK
[root@xsj redis-full-check]# ./redis-full-check --source 127.0.0.1:6379 --target 127.0.0.1:6389
INFO 2019-08-08-15:36:19](main.go:54): init log success
INFO 2019-08-08-15:36:19](full_check.go:238): sourceDbType=0, p.sourcePhysicalDBList=[meaningless]
INFO 2019-08-08-15:36:19](full_check.go:243): db=0:keys=3
INFO 2019-08-08-15:36:19](full_check.go:253): ---------------- start 1th time compare
INFO 2019-08-08-15:36:19](full_check.go:278): start compare db 0
INFO 2019-08-08-15:36:20](full_check.go:203): stat:
times:1, db:0, dbkeys:3, finish:33%, finished:true
KeyScan:{3 3 0}
KeyConflictInProcess|string|value|{1 1 0}

INFO 2019-08-08-15:36:20](full_check.go:250): wait 5 seconds before start
INFO 2019-08-08-15:36:25](full_check.go:253): ---------------- start 2th time compare
INFO 2019-08-08-15:36:25](full_check.go:278): start compare db 0
INFO 2019-08-08-15:36:26](full_check.go:203): stat:
times:2, db:0, finished:true
KeyScan:{1 1 0}
KeyConflictInProcess|string|value|{1 1 0}

INFO 2019-08-08-15:36:26](full_check.go:250): wait 5 seconds before start
INFO 2019-08-08-15:36:31](full_check.go:253): ---------------- start 3th time compare
INFO 2019-08-08-15:36:31](full_check.go:278): start compare db 0
INFO 2019-08-08-15:36:32](full_check.go:203): stat:
times:3, db:0, finished:true
KeyScan:{1 1 0}
KeyConflictAtLast|string|value|{1 1 0}

INFO 2019-08-08-15:36:32](full_check.go:323): --------------- finished! ----------------
all finish successfully, totally 3 key(s) and 0 field(s) conflict
[root@xsj redis-full-check]# sqlite3 result.db.3 
SQLite version 3.7.17 2013-05-20 00:56:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> select * from key;
1|name|string|value|0|2|4

  由上文验证可知,redis-full-check可以对比出源端与目标端的差异key。

参考链接:
https://yq.aliyun.com/articles/691794
https://yq.aliyun.com/articles/690463?spm=a2c4e.11155435.0.0.1cbe7bc170JysP

网友评论

登录后评论
0/500
评论
梓杰
+ 关注
所属团队号: 袋鼠云技术团队