CROSSSLOT 是 Redis Cluster 上多 key 命令的常见拦路虎。这篇用 Redis 8.10.2 的 3 主节点集群在本机复现:同一个节点上的 key 为什么也不行、19 个命令的清单、hash tag 的规则和容易写错的情形、事务与 Lua 里什么时候报,以及 hash tag 的代价。
user:3 槽位 2648 所在节点 8210
user:1 槽位 10778 所在节点 8211
user:0 槽位 14907 所在节点 8212
user:7 槽位 2780 所在节点 8210
> MGET user:3 user:1
(error) CROSSSLOT Keys in request don't hash to the same slot
> MGET user:3 user:3
1) "v-user:3"
2) "v-user:3"
对照:同一个节点上、但槽位不同的两个 key 也不行(判断标准是槽位,不是节点):
> MGET user:3 user:7
(error) CROSSSLOT Keys in request don't hash to the same slot判断标准是槽位而不是节点。
MGET -> CROSSSLOT MSET -> CROSSSLOT DEL -> CROSSSLOT UNLINK -> CROSSSLOT EXISTS -> CROSSSLOT TOUCH -> CROSSSLOT SUNION -> CROSSSLOT SINTER -> CROSSSLOT SUNIONSTORE -> CROSSSLOT RENAME -> CROSSSLOT COPY -> CROSSSLOT LMOVE -> CROSSSLOT SMOVE -> CROSSSLOT BITOP -> CROSSSLOT PFMERGE -> CROSSSLOT ZUNIONSTORE -> CROSSSLOT SORT -> CROSSSLOT WATCH -> CROSSSLOT BLPOP -> CROSSSLOT
MGET、MSET、DEL、EXISTS、RENAME、LMOVE、ZUNIONSTORE、WATCH 等多 key 命令都受限。
CLUSTER KEYSLOT user:1:name -> 12440
CLUSTER KEYSLOT user:1:age -> 13180
CLUSTER KEYSLOT {user:1}:name -> 10778
CLUSTER KEYSLOT {user:1}:age -> 10778
CLUSTER KEYSLOT {user:2}:name -> 6777
CLUSTER KEYSLOT order{1} -> 9842
CLUSTER KEYSLOT order{2} -> 5649
CLUSTER KEYSLOT {}user:1 -> 8914
CLUSTER KEYSLOT a{}b -> 13694
CLUSTER KEYSLOT {{x}} -> 11068
CLUSTER KEYSLOT {x}{y} -> 16287
CLUSTER KEYSLOT foo{bar}{zap} -> 5061
CLUSTER KEYSLOT foo{}{bar} -> 8363
同槽位的一组 key 可以 MGET / MSET / 事务:
(槽位 10778 在节点 8211 上,命令发给它)
> MSET {user:1}:name alice {user:1}:age 30
"OK"
> MGET {user:1}:name {user:1}:age
1) "alice"
2) "30"只哈希花括号里的内容;空花括号时整个 key 哈希。
> MULTI
"OK"
> SET user:3 1
"QUEUED"
> SET user:7 2
"QUEUED"
> EXEC
(error) CROSSSLOT Keys in request don't hash to the same slot
> EVAL return redis.call('MGET', KEYS[1], KEYS[2]) 2 user:3 user:1
(error) CROSSSLOT Keys in request don't hash to the same slot
> EVAL return redis.call('MGET', KEYS[1], KEYS[2]) 2 user:3 user:7
(error) CROSSSLOT Keys in request don't hash to the same slot
> EVAL return redis.call('GET', ARGV[1]) 0 user:1
(error) ERR Script attempted to access a non local key in a cluster node script: 2b607d3eeffe20cb8536f3e513a6fe42580a3d6e, on @user_script:1.
> EVAL return redis.call('MGET', KEYS[1], KEYS[2]) 2 {user:1}:name {user:1}:age
1) "alice"
2) "30"MULTI 到 EXEC 才报;Lua 的 KEYS 要同槽位,脚本里偷用别的 key 是另一种错误。
3000 个 {order}:i :各节点的 key 数 = [0, 0, 3000] 槽位 = 16025
3000 个 plain:i :各节点的 key 数 = [990, 1010, 1000]
读 200 个 key(本机回环,7 次取中位数):逐个 GET 6.75 ms;同 tag 一次 MGET 0.22 ms;按槽位分组 MGET(200 个槽位 = 200 次请求)6.75 ms同 tag 的 key 都在一个节点,热点与容量要自己负责。