KV-Probe:通用 KV 数据库测试套件
如果你正在做 Redis 兼容型 KV 数据库的开发、选型或运维,你大概率遇到过这样的窘境:想验证数据写进去到底读不读得对,得手写一堆脚本;想压个 QPS,redis-benchmark 又覆盖不到你关心的数据类型和读写混合场景;想确认你的实现到底兼不兼容 Redis 语义,只能一个命令一个命令地试。
KV-Probe 把这些散落的需求收敛成了一个命令行工具。
| 一、它解决的是什么问题 |
近几年 Redis 协议几乎成了 KV 存储的事实标准,市面上涌现出大量「Redis 兼容」的数据库——有的换了存储引擎,有的做了持久化改造,有的干脆重写了内核。但「兼容」两个字说起来轻巧,落到工程上却处处是坑:
•数据到底一致吗?写进去的 Hash 字段、ZSet 分数、List 顺序,读回来还是原样吗?
•性能到底如何?单纯的 SET/GET 数字没有意义,真实业务是读写混合的、是带 Pipeline 的、是多种数据结构并存的。
•纯读能力有多强?很多场景(缓存、热点查询)是读远大于写的,但一旦有 miss,测出来的数字就失真了。
•API 语义真的对齐了吗?边界条件、错误返回、类型转换……这些细节往往才是兼容性的分水岭。
KV-Probe 用四个子命令,分别对应上述四类任务,用一套统一的配置、日志和指标体系把它们串了起来。它本身是一个纯粹的命令行程序,用 Go 1.21 编写,底层基于成熟的 go-redis v9 驱动,对任何支持 Redis 协议(RESP)的服务端都能直接探测。
| 二、四大核心能力 |
1. check — 写后即读的数据一致性校验
这是 KV-Probe 的看家本领,也是默认子命令。它的逻辑很朴素却很关键:写入一个 Key 后立刻读回来,逐字节比对是否一致,覆盖 String、Hash、Set、ZSet、List 全部五种数据类型。
它提供了三种校验模式,兼顾覆盖度与成本:
Full(全量):每次写入都读回校验,100% 覆盖,适合正确性验收。
Sample(抽样):按比例(默认 10%)抽查,适合长时间跑巡检。
Both(混合):Worker 平均分为两组,一边全量一边抽样。
配合失败重试(指数退避、最多 3 次)和「服务不可用时长统计」,它不仅能发现「数据错了」,还能量化「服务挂了多久」。
# 20 并发、全量模式校验 Hash 类型 ./bin/kv-probe check -h 127.0.0.1 -p 6379 -c 20 -mode full -data-type hash # 同时校验多种数据类型,随机选择写读 ./bin/kv-probe check -data-type “string,hash,list” -c 10 -duration 30m
为什么「写后即读」这么关键?在分布式 KV 系统里,一致性问题往往不是「写不进去」,而是「写进去了却读不到、或读到了旧值」。主从复制延迟、集群模式下的路由错乱、内存淘汰把数据提前删掉、故障切换后的数据回退……这些问题在单元测试里很难复现,却会在真实负载下悄悄啃噬数据可靠性。check 把「写—读—比对」压缩成一个原子动作高频重复,正是为了把这类偶发的、时间窗口极窄的一致性缺口给逼出来。
它对不同数据类型的比对是「按语义」而非「按字节流」来的:Hash 会逐字段比对键值,Set 做集合等价判断(不关心顺序),ZSet 连成员的 score 都要一致,List 则严格校验元素顺序。这样才能真正验证一个兼容实现有没有把「有序集合」做成了「无序」,或者把 List 的插入顺序搞乱。
它还能当可用性探针用。内置的服务不可用检测基于「业务成功/失败」来驱动——连续多次操作失败即判定进入不可用状态,恢复后累加这一段不可用时长。换句话说,把它挂在生产旁路上长期跑抽样模式,你就得到了一个持续输出「可用率」的黑盒探针,比单纯 ping 端口要贴近真实业务体感得多。
2. bench — 对标 redis-benchmark 的性能压测
如果说 check 关心「对不对」,bench 关心的就是「快不快」。它对标 redis-benchmark,但把能力做得更贴合真实业务:
•顺序模式:逐个命令依次压测,看单命令的极限吞吐;
•混合并行模式:按你设定的读写比例(如 7:3)同时打读和写,还原真实负载;
•Pipeline 批量:一次网络往返打包多条命令,测批量吞吐;
•多命令、多数据类型:set/get/incr/hset/hget/hgetall/sadd/smembers/zadd/zrange/lpush/rpush/lrange 等一网打尽。
# 50 并发、100 万请求,压测 set + get ./bin/kv-probe bench -t set,get -c 50 -n 1000000 # 读写 7:3 的混合并行压测 ./bin/kv-probe bench -t set,get -mode parallel -rw-ratio 7:3 # Pipeline 批量为 10 的压测 ./bin/kv-probe bench -t set,get -pipeline 10
为什么不直接用 redis-benchmark?redis-benchmark 是个好工具,但它诞生于「测原生 Redis 单命令极限」的语境。而当你在评估一个兼容内核时,真正想知道的往往是:在贴近我业务负载形态的前提下,它能扛多少?比如「70% 读 30% 写混合打,Hash 结构为主,带 Pipeline」这种组合,bench 的混合并行模式 + 读写比例 + 多命令 + Pipeline 参数就能一次性还原出来,而不必拼凑多次单独测试再手工汇总。
bench 在启动压测前会先做连接池预热与健康探测,避免把「冷启动的连接建立开销」算进吞吐数字里,让结果更能反映稳态性能。压测过程中同样实时吐出 QPS 与 TP90/TP99/TP999,压完还有汇总报告——你既能看瞬时抖动,也能拿到一个可写进评测报告的总账。
一个实践建议:先用顺序模式逐个命令摸出各操作的性能上限(这一步能帮你识别出「哪个命令是瓶颈」),再用混合并行模式按业务真实读写比复现负载,最后叠加Pipeline看批量优化的收益空间。三步下来,你对这个 KV 的性能画像基本就完整了。
3. readperf — 100% 命中的极致读性能测试
这是一个很有巧思的子命令。测「纯读性能」时最怕的就是 miss——一旦读到不存在的 Key,测出来的延迟和吞吐就掺了水。readperf 用「两阶段」设计彻底规避了这个问题:
Fill 阶段:先批量写入指定数量的 Key,并把 Key 落盘到 CSV 数据文件;
Read 阶段:只对这些确定存在的 Key 发起海量读取,保证100% 命中。
写入的 Key 落盘后可以复用——下次加 -skip-fill 就能跳过写入直接压读,省去重复铺数据的时间。五种数据类型各自使用对应的写读命令(如 Hash 用 HSET 写、HGETALL 读)。
# 写 10 万 key,再读 100 万次(默认 string) ./bin/kv-probe readperf -fill-keys 100000 -n 1000000 # 大规模:100 万 key、200 并发、Pipeline 10 ./bin/kv-probe readperf -fill-keys 1000000 -c 200 -n 10000000 -pipeline 10 # 复用已有数据文件,跳过写入直接压读 ./bin/kv-probe readperf -t hash -skip-fill -data-file data/readperf.csv -c 100 -n 5000000
为什么单独做一个读性能命令?因为「读」的性能故事和「写」完全不同。读密集场景(缓存层、热点查询、只读副本)里,你想知道的是「在数据确定命中的前提下,读路径的纯粹吞吐与延迟」。可一旦测试里混进了 miss,被测系统对「不存在的 key」往往走的是另一条更快或更慢的代码路径,测出来的数字既不代表命中也不代表未命中,纯属噪声。readperf 用两阶段设计把这个变量彻底钉死:读的每一个 key 都是刚写进去的、确定存在的,命中率恒为 100%。
Key 落盘到 CSV 带来的价值不止「省一次铺数据」。它意味着 fill 和 read 可以解耦:你可以头一天灌好一亿条数据,之后连续几天用 -skip-fill 反复压读、对比不同参数下的表现,数据集始终一致,结果才有可比性。命令还内置了 -strict 严格模式,一旦出现意料之外的 key not found 就计为错误,帮你及时发现「数据在读阶段被淘汰/丢失」这类隐蔽问题。
4. test — Redis API 语义兼容性测试
前三个命令关注数据与性能,test 则专注「语义是否对齐」。它内置了一套测试框架,按数据类型组织成 Suite,逐个 case 验证 API 行为,其中还包含从 Redis 官方 TCL 测试用例移植而来的套件(string-tcl、hash-tcl 等),把 Redis 自己用来自测的严苛用例搬了过来。
控制台只打印简洁的摘要(多少通过、多少失败),详细的失败信息——包括期望值与实际值——则完整落到 logs/test.log,方便回溯定位。
# 跑全部套件 ./bin/kv-probe test -h 127.0.0.1 -p 6379 # 只跑 TCL 移植的套件 ./bin/kv-probe test -suites string-tcl,hash-tcl,set-tcl,zset-tcl,list-tcl # 失败即停 + 重复执行 3 次 ./bin/kv-probe test -fail-fast -repeat 3
「兼容」的成败往往藏在边界里。SET 能跑通不代表兼容,真正区分高下的是那些细节:SETEX 的负数过期时间该报什么错?对一个 String 执行 LPUSH 会不会正确返回类型错误?ZADD 的 NX/XX/GT/LT 修饰符语义对不对?EXPIRE 精度如何?这些正是移植自 Redis 官方 TCL 测试集的用例要覆盖的地方——把 Redis 自己用来保证质量的严苛断言直接搬过来,等于让被测系统去考 Redis 的「原厂卷子」。
test 的输出设计也很务实:控制台只给一屏能看懂的摘要(每个套件通过/失败/跳过多少、耗时多少),不刷屏;而每一个失败 case 的期望值与实际值都完整落到 logs/test.log(追加模式,保留历史)。这样 CI 里一眼就能看出「这次挂了几个」,需要定位时再翻日志看「具体哪里不一样」。配合 -fail-fast(快速失败)和 -repeat(重复执行、抓偶发问题),它能很自然地嵌进持续集成流程,成为兼容性回归的守门员。
| 三、几种典型工作流 |
把四个命令组合起来,能覆盖研发到运维的完整链路。这里给几个真实场景的「套路」:
场景一:自研 KV 内核的上线前验收
# ① 先验语义:确保 API 行为对齐 Redis(含 TCL 严苛用例) ./bin/kv-probe test -suites string,hash,set,zset,list,string-tcl,hash-tcl # ② 再验一致性:全量模式高并发跑一段,逼出写读不一致 ./bin/kv-probe check -c 50 -mode full -data-type “string,hash,zset,list” -duration 30m # ③ 最后验性能:混合读写压测 + 极致读性能 ./bin/kv-probe bench -t set,get -mode parallel -rw-ratio 7:3 -c 100 -n 5000000 ./bin/kv-probe readperf -fill-keys 1000000 -c 200 -n 10000000 -pipeline 10
语义→一致性→性能,三道关卡下来,一次改动到底动没动到筋骨,心里就有数了。
场景二:多个「Redis 兼容」产品横向选型
用同一套参数分别打向不同候选,把各自的 QPS、TP99、读性能、兼容性通过率拉进一张表对比。因为工具、负载、指标口径完全一致,得出的结论才经得起推敲,而不是「A 用这个脚本测的、B 用那个工具测的」这种没法比的数据。
场景三:生产旁路的长期一致性巡检
# 抽样模式 + 长时间运行,低压力挂在生产环境旁路 ./bin/kv-probe check -mode sample -sample-rate 0.05 -c 10 -duration 0
-duration 0 表示无限运行,直到收到停止信号。挂上后指标持续吐给 Prometheus,配好告警,它就成了一个 7×24 的一致性与可用性探针。
| 四、它和同类工具的关系 |
有人会问:有了 redis-cli、redis-benchmark、还有各种一致性测试框架,为什么还需要 KV-Probe?
答案是收敛与聚焦。这些工具各管一段:redis-benchmark 管压测、手写脚本管一致性、专门的兼容性测试套件又是另一套环境。而在「评估一个 Redis 兼容 KV」这个具体目标下,你需要的恰恰是把它们串成一条流水线——用同一套连接配置、同一套数据生成规则、同一套指标口径,一站式回答「对不对、快不快、稳不稳、兼不兼容」四个问题。KV-Probe 不追求在任何单一维度上超越专精工具,它的价值在于让这四件事在同一个工具里闭环,省掉你在多个工具间搬运数据、对齐口径的隐性成本。
| 结语 |
KV-Probe 不追求华丽,它追求的是「把一件事做透」——给 Redis 兼容型 KV 数据库一个统一、可靠、可观测的探测入口。无论你是在为自研 KV 内核做上线前的质量把关,还是在为一次数据库选型做横向对比,抑或只是想搞清楚手上这个「Redis 兼容」的服务到底兼不兼容,它都能让你少写很多一次性脚本,把精力放在真正重要的判断上。
360智汇云是企业智数云底座,以"智-数-云"三大核心底座为支柱,以贯穿全程的 “观测与管控” 为神经中枢,全链路赋能企业数智基建在 “用、运、管、看、维” 五维生命周期中实现价值闭环。提供数据库、中间件、存储、大数据、人工智能、计算等多种产品服务以及一站式解决方案,让每一份IT投入都转化为智能生产力。
官网:https://zyun.360.cn
