最近在实操部署 Redis Cluster,我在了解其中原理的时候发现:多条 key 会被分散存储到不同的节点上,所以像 MGET key1 key2 这种跨 key 的命令就不能用了——因为 key1 和 key2 可能不在同一个节点。
然后教程接着讲,用 Hash Tag 可以解决这个问题。你只需要把 key 写成 {user:1}:name 和 {user:1}:age,它们就会被放到同一个节点上,MGET 也能正常用了。
这里的原理并不复杂。Redis Cluster 将数据分布到 16384 个哈希槽,槽号由 CRC16(key) % 16384 计算得出——这是默认行为。而 Hash Tag 的规则只有一条:如果 key 中包含 {...},则只对花括号内的部分做 CRC16 计算。
| Key | 参与 CRC16 计算的部分 | 结果 |
|---|---|---|
user:1:name | user:1:name | 随机分布 |
{user:1}:name | user:1 | 两个 key 落到同一个槽 |
{user:1}:age | user:1 | 同上 |
所以 {user:1}:name 和 {user:1}:age 的计算输入都是 user:1,槽号自然相同,从存入那一刻起就在同一个节点上。这就是 Hash Tag 的全部机制,几句话就能说清。
看到这里的时候,我脑子里冒出一个奇怪的理解。
我以为,不管你有没有用 Hash Tag,key 都是被分散存储的。Hash Tag 的作用只是在读取的时候,能把散在各处的 key”归拢”回来看作一组——像是一种检索魔法。所以当时我特别困惑:既然都存到不同节点了,Redis 凭什么能通过一个 Tag 就把它俩一起取回来?
这个困惑折磨了我挺长时间。直到后来重新翻文档,才意识到一个根本性的误解。
Hash Tag 不是在取的时候才生效的,它在存的时候就决定了 key 的位置。 不是“分开存然后再合起来取”,而是”一开始就没分开”。
那篇教程并没有明确指出这两种情况是两个独立的分支——它先讲了”默认会分散存储,多 key 命令会出问题”,紧接着给了 Hash Tag 的解法,但没有在中间做一个清晰的切断。我脑子自动把两个场景拼接在一起,产生了一个完全错误的模型。
现在回想,如果教程当时多写一句——“使用 Hash Tag 存储数据可以让相关 key 落到同一个槽,从而避免遇到跨节点问题”——这个困惑从一开始就不会存在。