槽位号是怎么来的?为什么是 16384 而不是 65536?这篇不背答案:先用自己写的 CRC16 去对拍服务器的 CLUSTER KEYSLOT,再量 100 万个 key 在槽里的分布,最后伪造一个集群总线对端,抓下节点发来的第一条消息,看看那个「槽位位图」到底有多大。
crc16("123456789") = 0x31C3(XMODEM 标准校验值是 0x31C3)
随机 key 20000 个(含 { } 字符):自己算的槽位与服务器一致 20000 个,不一致 0 个 []
CLUSTER KEYSLOT foo → 12182 自己算 12182
CLUSTER KEYSLOT bar → 5061 自己算 5061
CLUSTER KEYSLOT user:1000 → 1649 自己算 1649
CLUSTER KEYSLOT {user1}:a → 8106 自己算 8106
CLUSTER KEYSLOT {user1}:b → 8106 自己算 8106
CLUSTER KEYSLOT a{}b → 13694 自己算 13694槽位 = CRC16(key) mod 16384,CRC16 是 XMODEM 变体(多项式 0x1021,初值 0)。我用 Python 写了一个表驱动的实现,输入 "123456789" 得到标准校验值 0x31C3;随机生成的 20000 个 key(包含花括号)全部与 CLUSTER KEYSLOT 一致,包括 hash tag 的规则({user1}:a 与 {user1}:b 都是 8106;a{}b 里花括号是空的,整个 key 参与计算)。
平均 61.04 个/槽;最小 43,最大 79;标准差 6.74(泊松期望 7.81) 卡方统计量 = 12177(自由度 16383;若完全均匀随机,期望 ≈ 16383,标准差 ≈ 181) 三段槽位(0–5460 / 5461–10922 / 10923–16383)各占 key: [333354, 333279, 333367] ['33.34%', '33.33%', '33.34%']
100 万个连续 key(user:0…user:999999)在 16384 个槽里,平均 61.0 个,最少 43、最多 79,标准差 6.74,低于纯随机撒点的期望 7.81;卡方 12177 也低于期望 16383(±181)。也就是说,对「前缀相同、后缀是递增数字」这类规则的 key,CRC16 的分散效果比随机更均匀。这一点只对我测的这批 key 成立,不代表任意 key 集合。三段槽位各拿到约三分之一的 key。
签名 b'RCmb';总长度 totlen = 2256 字节;消息类型编号 2(由 CLUSTER MEET 触发的第一条消息);gossip 条目数 count = 0 sender = f9d3e14e…(CLUSTER MYID = f9d3e14e…,一致: True);位图 2048 字节里置位的槽数 = 101(我分配了 0–100,共 101 个) 位图前 16 字节(十六进制):ffffffffffffffffffffffff1f000000(槽 0–100 → 前 12 个字节全 ff、第 13 个字节低位置位)
做法:开一个单节点的集群实例,分配槽 0–100,再让它 CLUSTER MEET 一个我用 socket 伪造的对端(监听总线端口 17331)。节点连过来,发来的第一条消息以 RCmb 开头,总长度 2256 字节;从偏移 80 开始有 2048 字节——那是本节点的槽位位图,16384 位,我分配的 101 个槽对应 101 个置位。发送方 ID 也与 CLUSTER MYID 一致。
这条消息里没有 gossip 条目(count = 0),因为节点还不认识别人;有邻居时会在头部之后追加条目。位图是固定大小,每次心跳都带:槽位数是 16384 时是 2048 字节,是 65536 时会是 8192 字节。这就是常被引用的理由之一;另外「集群不超过约 1000 个主节点」的设计假设也是官方说法,我没有做这一部分的实验。