一、redis-full-check介绍:
redis-full-check是阿里云Redis&MongoDB团队开源的用于校验2个redis数据是否一致的工具,通常用于redis数据迁移(redis-shake)后正确性的校验。
支持:单节点、主从版、集群版、带proxy的云上集群版(阿里云)之间的同构或者异构对比,版本支持2.x-5.x。
1.1基本原理:
redis-full-check通过全量对比源端和目的端的redis中的数据的方式来进行数据校验,其比较方式通过多轮次比较:
每次都会抓取源和目的端的数据进行差异化比较,记录不一致的数据进入下轮对比(记录在sqlite3 db中)。然后通过多伦比较不断收敛,减少因数据增量同步导致的源库和目的库的数据不一致。
最后sqlite中存在的数据就是最终的差异结果。
redis-full-check对比的方向是单向:抓取源库A的数据,然后检测是否位于B中,反向不会检测,也就是说,它检测的是源库是否是目的库的子集。
如果希望对比双向,则需要对比2次,第一次以A为源库,B为目的库,第二次以B为源库,A为目的库。
下图是基本的数据流图,redis-full-check内部分为多轮比较,也就是框所指示的部分。
每次比较,会先抓取比较的key,第一轮是从源库中进行抓取,后面轮次是从sqlite3 db中进行抓取;
抓取key之后是分别抓取key对应的field和value进行对比,然后将存在差异的部分存入sqlite3 db中,用于下次比较。
不一致类型:
redis-full-check判断不一致的方式主要分为2类:key不一致和value不一致。
key不一致:
key不一致主要分为以下几种情况:
lack_target : key存在于源库,但不存在于目的库。
type: key存在于源库和目的库,但是类型不一致。
value: key存在于源库和目的库,且类型一致,但是value不一致。
value不一致:
不同数据类型有不同的对比标准:
string: value不同。
hash: 存在field,满足下面3个条件之一:
field存在于源端,但不存在与目的端。
field存在于目的端,但不存在与源端。
field同时存在于源和目的端,但是value不同。
set/zset:与hash类似。
list: 与hash类似。
field冲突类型有以下几种情况(只存在于hash,set,zset,list类型key中):
lack_source: field存在于源端key,field不存在与目的端key。
lack_target: field不存在与源端key,field存在于目的端key。
value: field存在于源端key和目的端key,但是field对应的value不同
1.2比较原理:
对比模式(comparemode)有三种可选:
KeyOutline:只对比key值是否相等。
ValueOutline:只对比value值的长度是否相等。
FullValue:对比key值、value长度、value值是否相等。
对比会进行comparetimes轮(默认comparetimes=3)比较:
第一轮,首先找出在源库上所有的key,然后分别从源库和目的库抓取进行比较。
第二轮开始迭代比较,只比较上一轮结束后仍然不一致的key和field。
对于key不一致的情况,包括lack_source ,lack_target 和type,从源库和目的库重新取key、value进行比较。
value不一致的string,重新比较key:从源和目的取key、value比较。
value不一致的hash、set和zset,只重新比较不一致的field,之前已经比较且相同的filed不再比较。这是为了防止对于大key情况下,如果更新频繁,将会导致校验永远不通过的情况。
value不一致的list,重新比较key:从源和目的取key、value比较。
每轮之间会停止一定的时间(Interval)。
对于hash,set,zset,list大key处理采用以下方式:
len <= 5192,直接取全量field、value进行比较,使用如下命令:hgetall,smembers,zrange 0 -1 withscores,lrange 0 -1。
len > 5192,使用hscan,sscan,zscan,lrange分批取field和value。
二、redis-full-check中主要参数如下:
-s, --source=SOURCE 源redis库地址(ip:port),如果是集群版,那么需要以分号(;)分割不同的db,只需要配置主或者从的其中之一。例如:10.1.1.1:1000;10.2.2.2:2000;10.3.3.3:3000。
-p, --sourcepassword=Password 源redis库密码
--sourceauthtype=AUTH-TYPE 源库管理权限,开源reids下此参数无用。
--sourcedbtype= 源库的类别,0:db(standalone单节点、主从),1: cluster(集群版),2: 阿里云
--sourcedbfilterlist= 源库需要抓取的逻辑db白名单,以分号(;)分割,例如:0;5;15表示db0,db5和db15都会被抓取
-t, --target=TARGET 目的redis库地址(ip:port)
-a, --targetpassword=Password 目的redis库密码
--targetauthtype=AUTH-TYPE 目的库管理权限,开源reids下此参数无用。
--targetdbtype= 参考sourcedbtype
--targetdbfilterlist= 参考sourcedbfilterlist
-d, --db=Sqlite3-DB-FILE 对于差异的key存储的sqlite3 db的位置,默认result.db
--comparetimes=COUNT 比较轮数
-m, --comparemode= 比较模式,1表示全量比较,2表示只对比value的长度,3只对比key是否存在,4全量比较的情况下,忽略大key的比较
--id= 用于打metric
--jobid= 用于打metric
--taskid= 用于打metric
-q, --qps= qps限速阈值
--interval=Second 每轮之间的时间间隔
--batchcount=COUNT 批量聚合的数量
--parallel=COUNT 比较的并发协程数,默认5
--log=FILE log文件
--result=FILE 不一致结果记录到result文件中,格式:'db diff-type key field'
--metric=FILE metric文件
--bigkeythreshold=COUNT 大key拆分的阈值,用于comparemode=4
-f, --filterlist=FILTER 需要比较的key列表,以分号(;)分割。例如:"abc*|efg|m*"表示对比'abc', 'abc1', 'efg', 'm', 'mxyz',不对比'efgh', 'p'。
-v, --version
三、RedisFullCheck使用演示:
本文主要介绍如何使用RedisFullCheck
RedisFullCheck可以用于校验两个redis的数据是否一致,包括主从版、单节点、集群版,以及一些云上的带proxy的集群版(阿里云,腾讯云)
3.1下载安装:
cd /data/soft
wget https://github.com/alibaba/RedisFullCheck/releases/download/release-v1.4.7-20191203/redis-full-check-1.4.7.tar.gz
tar xf redis-full-check-1.4.7.tar.gz
cd redis-full-check-1.4.7
cp redis-full-check /usr/local/redis/bin/
[root@localhost redis-full-check-1.4.7]# which redis-full-check
/usr/local/redis/bin/redis-full-check
redis-cli -h 127.0.0.1 -p 6986 -a 'Y2hJKSGtuEq'
redis-cli -h 127.0.0.1 -p 6987 -a 'Y2hJKSGtuEq'
3.2redis实例配置主从复制:
6986 为master
6987 为slave
对比2个主从版/单节点:
redis-full-check -s 127.0.0.1:6986 -p 'Y2hJKSGtuEq' -t 127.0.0.1:6987 -a 'Y2hJKSGtuEq' -m 1
[root@localhost redis-full-check-1.4.7]# redis-full-check -s 127.0.0.1:6986 -p 'Y2hJKSGtuEq' -t 127.0.0.1:6987 -a 'Y2hJKSGtuEq' -m 1
[INFO 2019-12-29-20:50:32 main.go:65]: init log success
[INFO 2019-12-29-20:50:32 main.go:164]: configuration: {127.0.0.1:6986 Y2hJKSGtuEq auth 0 -1 127.0.0.1:6987 Y2hJKSGtuEq auth 0 -1 result.db 3 1 unknown unknown unknown 15000 5 256 5 false 16384 20445 false}
[INFO 2019-12-29-20:50:32 main.go:166]: ---------
[INFO 2019-12-29-20:50:32 full_check.go:238]: sourceDbType=0, p.sourcePhysicalDBList=[meaningless]
[INFO 2019-12-29-20:50:32 full_check.go:243]: db=0:keys=2
[INFO 2019-12-29-20:50:32 full_check.go:253]: ---------------- start 1th time compare
[INFO 2019-12-29-20:50:32 full_check.go:278]: start compare db 0
[INFO 2019-12-29-20:50:32 scan.go:20]: build connection[source redis addr: [127.0.0.1:6986]]
[INFO 2019-12-29-20:50:33 full_check.go:203]: stat:
times:1, db:0, dbkeys:2, finish:33%, finished:true
KeyScan:{2 2 0}
KeyEqualInProcess|string|equal|{2 2 0}
[INFO 2019-12-29-20:50:33 full_check.go:250]: wait 5 seconds before start
[INFO 2019-12-29-20:50:38 full_check.go:253]: ---------------- start 2th time compare
[INFO 2019-12-29-20:50:38 full_check.go:278]: start compare db 0
[INFO 2019-12-29-20:50:38 full_check.go:203]: stat:
times:2, db:0, finished:true
KeyScan:{0 0 0}
[INFO 2019-12-29-20:50:38 full_check.go:250]: wait 5 seconds before start
[INFO 2019-12-29-20:50:43 full_check.go:253]: ---------------- start 3th time compare
[INFO 2019-12-29-20:50:43 full_check.go:278]: start compare db 0
[INFO 2019-12-29-20:50:43 full_check.go:203]: stat:
times:3, db:0, finished:true
KeyScan:{0 0 0}
[INFO 2019-12-29-20:50:43 full_check.go:328]: --------------- finished! ----------------
all finish successfully, totally 0 key(s) and 0 field(s) conflict
由此看到主从redis数据是一致的
3.3比对流程说明:
默认会比对三次,产生3个db文件:
[root@localhost redis-full-check-1.4.7]# ls
ChangeLog redis-full-check result.db.1 result.db.2 result.db.3
redis-full-check -s 127.0.0.1:6986 -p 'Y2hJKSGtuEq' -t 127.0.0.1:6987 -a 'Y2hJKSGtuEq' -m 1 --result=result
[root@localhost redis-full-check-1.4.7]# redis-full-check -s 127.0.0.1:6986 -p 'Y2hJKSGtuEq' -t 127.0.0.1:6987 -a 'Y2hJKSGtuEq' --result=result
[INFO 2019-12-29-21:15:54 main.go:65]: init log success
[INFO 2019-12-29-21:15:54 main.go:164]: configuration: {127.0.0.1:6986 Y2hJKSGtuEq auth 0 -1 127.0.0.1:6987 Y2hJKSGtuEq auth 0 -1 result.db result 3 2 unknown unknown unknown 15000 5 256 5 false 16384 20445 false}
[INFO 2019-12-29-21:15:54 main.go:166]: ---------
[INFO 2019-12-29-21:15:54 full_check.go:238]: sourceDbType=0, p.sourcePhysicalDBList=[meaningless]
[INFO 2019-12-29-21:15:54 full_check.go:243]: db=0:keys=4
[INFO 2019-12-29-21:15:54 full_check.go:253]: ---------------- start 1th time compare
[INFO 2019-12-29-21:15:54 full_check.go:278]: start compare db 0
[INFO 2019-12-29-21:15:54 scan.go:20]: build connection[source redis addr: [127.0.0.1:6986]]
[INFO 2019-12-29-21:15:55 full_check.go:203]: stat:
times:1, db:0, dbkeys:4, finish:33%, finished:true
KeyScan:{4 4 0}
KeyConflictInProcess|string|lack_target|{2 2 0}
[INFO 2019-12-29-21:15:55 full_check.go:250]: wait 5 seconds before start
[INFO 2019-12-29-21:16:00 full_check.go:253]: ---------------- start 2th time compare
[INFO 2019-12-29-21:16:00 full_check.go:278]: start compare db 0
[INFO 2019-12-29-21:16:01 full_check.go:203]: stat:
times:2, db:0, finished:true
KeyScan:{2 2 0}
KeyConflictInProcess|string|lack_target|{2 2 0}
[INFO 2019-12-29-21:16:01 full_check.go:250]: wait 5 seconds before start
[INFO 2019-12-29-21:16:06 full_check.go:253]: ---------------- start 3th time compare
[INFO 2019-12-29-21:16:06 full_check.go:278]: start compare db 0
[INFO 2019-12-29-21:16:07 full_check.go:203]: stat:
times:3, db:0, finished:true
KeyScan:{2 2 0}
KeyConflictAtLast|string|lack_target|{2 2 0}
[INFO 2019-12-29-21:16:07 full_check.go:328]: --------------- finished! ----------------
all finish successfully, totally 4 key(s) and 0 field(s) conflict
[root@localhost redis-full-check-1.4.7]# ls
ChangeLog redis-full-check result result.db.1 result.db.2 result.db.3
[root@localhost redis-full-check-1.4.7]# less result
0 lack_target test003
0 lack_target test004
[root@localhost redis-full-check-1.4.7]#
官方文档:https://yq.aliyun.com/articles/690463
https://github.com/alibaba/RedisFullCheck
https://github.com/alibaba/RedisFullCheck/wiki/%E7%AC%AC%E4%B8%80%E6%AC%A1%E4%BD%BF%E7%94%A8%EF%BC%8C%E5%A6%82%E4%BD%95%E8%BF%9B%E8%A1%8C%E9%85%8D%E7%BD%AE%EF%BC%9F