bigywater.tech
Redis · 72

Redis Cluster Hash Tag

Redis Cluster 里多 key 命令(MSET、事务、Lua)的前提是所有 key 在同一个槽里。Hash tag 是让它们「被迫」同槽的办法:只对 key 里花括号中的部分算 CRC16。这篇把 tag 的解析规则逐条对照服务器验证,再看它最典型的代价——热点:一个 tag 就是一个槽,一个槽只能在一个节点上。

槽 8106
{user1}:profile / :orders / :cart 三个 key 的共同槽位
12 / 12
hash tag 解析规则的推断与服务器 CLUSTER KEYSLOT 一致的用例数
100000 / 0 / 0
10 万个 key 全用同一个 tag 时,三个主库的 DBSIZE

没有 tag vs 有 tag

实测 · 本机执行输出
  user1:profile      → 槽 12715
  user1:orders       → 槽 1543
  user1:cart         → 槽 9467
  {user1}:profile    → 槽 8106
  {user1}:orders     → 槽 8106
  {user1}:cart       → 槽 8106

没有 tag 时同一个用户的三个 key 落在三个不同的槽(12715、1543、9467);加上 {user1} 之后,三个 key 都只用 user1 计算,槽位一致(8106)。

解析规则

实测 · 本机执行输出
key             服务器槽位          参与计算的部分     推断槽位   一致
'a{b}c'          3300              'b'     3300    ✓
'{b}'            3300              'b'     3300    ✓
'{}a'           10875            '{}a'    10875    ✓
'{a'            10276             '{a'    10276    ✓
'a}'             5921             'a}'     5921    ✓
'{{a}}'         10276             '{a'    10276    ✓
'{a}{b}'        15495              'a'    15495    ✓
'{a}}'          15495              'a'    15495    ✓
'x{}{y}'        14166         'x{}{y}'    14166    ✓
'a{b'           13340            'a{b'    13340    ✓
'{a}b}c'        15495              'a'    15495    ✓
''                  0               ''        0    ✓

我把「取第一个 { 和它之后第一个 } 之间的内容,内容为空则整个 key 参与计算」这条规则实现出来,对 12 个边界写法逐条与 CLUSTER KEYSLOT 对照,全部一致。几个容易搞错的:{}a(空花括号)整个 key 参与;{{a}} 取的是 {a;{a}{b} 只看第一组;缺右括号的 {a 整个 key 参与。空字符串的槽位是 0。

代价:热点

实测 · 本机执行输出
各主库 DBSIZE: {7360: 0, 7361: 100000, 7362: 0}   CLUSTER COUNTKEYSINSLOT 8106 = 100000 (向该槽的主库查询)

10 万个 key 都用 {user1},它们全在槽 8106,也就全在同一个主库(三个主库 DBSIZE 为 0 / 100000 / 0)。槽是迁移的最小单位,这个热点槽没法被拆分到别的节点——只能把整个槽搬走。

折中:把 tag 拆细

实测 · 本机执行输出
各主库 DBSIZE: {7360: 28124, 7361: 35937, 7362: 35939}
64 个 tag 落在 64 个不同的槽

把 tag 拆成 64 份({user1:0}…{user1:63}):10 万个 key 分散到 64 个不同的槽,三个主库 28124 / 35937 / 35939,不是严格均等(64 个 tag 掉进三个槽段是随机的)。代价是:只有落在同一个 tag 里的 key 才能一起做多 key 操作。所以 tag 的粒度就是「你需要多 key 原子性的最大范围」。

分步演示:tag 的规则与代价

Redis Cluster Hash Tag
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。3 主 3 从;规则验证用 12 个用例,分布实验用 10 万个 key
    • 没有验证:非 ASCII / 二进制 key 的 tag 行为、tag 与 Pub/Sub 的关系、hash tag 对 MEMORY 或 SCAN 行为的影响
    京ICP备2026067502号-1京公网安备11010502063487号