go-redis EXISTS 两个键却返回 4,检查动态路由聚合
启用动态命令resolver后,同槽多键的整数回复可能按键重复相加。先确认配置与实际slot,保留重复key含义,不能把总数简单除以键数。
EXISTS {t}a {t}b 只检查两个实际存在的键,go-redis 却返回4;DEL 的删除数也可能比真正删除的键多。若应用启用了 NewDynamicResolver() 的策略路由,应先检查客户端的多槽整数结果聚合。
这条路径不是 Redis 对 EXISTS 的通常计数规则。默认resolver没有相同的核心命令策略,不能把所有集群客户端都算进来。
同一槽的回复被重复加入总数
受影响的策略会按槽发送子命令。例如两个键同属一个槽,服务端只收到一条 EXISTS,返回2。客户端却把这个完整的槽结果分别赋给该槽中的每个不同键,随后再次求和,于是变成4。
正式v9.23.0的 aggregateMultiSlotResults 与求和路径仍需注意这类映射。检查实际启用的resolver、命令策略和键的slot,不能只根据键名前缀推断同槽;Hash tag 只是决定slot的一种规则。
还要保留命令本来的重复键语义。EXISTS k k 会按参数中的重复出现计数,不能为了“去重修复”统一把输入变成集合。删除命令与存在性查询的语义也不同,不能共用一个未经区分的去重办法。
保留清楚的路由边界
若业务不需要这项自动多槽策略,可以在现有客户端配置入口恢复默认路由约定:同槽命令仍按原多键形式发送,跨槽则明确报告或由业务协调,不能静默把 CROSSSLOT 当成成功。恢复配置时保留原TLS、认证、超时和连接池参数,不临时新建一个只填地址的客户端。
确实需要跨槽操作时,可以在应用已经明确拥有的协调层按真实slot分组。每组保留原键序列和重复项,发送一次该槽的子命令;每条子命令的整数回复只计入一次。采用这条路线的客户端本身不能再把同一组交给受影响的动态求和路径。
子命令失败需要单独保留,不能用成功部分的总数覆盖错误。跨槽写入可能已经完成其中一部分,重试还要遵守原有幂等与补偿规则。分组执行不提供跨槽原子性,单次总数也不是整个集群某一时刻的快照。
不要把返回总数除以键数。同槽键数、缺失键、重复键与多个槽混合后,各组权重不同,没有一个统一除数能恢复真实结果。
先用只读计数核对
在测试集群覆盖同槽两个键、同槽缺失键、多个槽和重复键,先用 EXISTS 比较原命令与各槽回复。涉及 DEL、UNLINK 时只能操作明确的测试前缀,不能在生产键上重复删除来验证计数。
后续采用依赖修正时,应确认正式版本对整数回复按槽聚合,而非仍按键分发后求和。错误计数消失后,仍需检查业务是否曾据此错误确认删除完成或触发补偿,不把客户端数字正确等同于历史状态自动恢复。


