说来惭愧,今天我才了解到 Redis Cluster 模式下 MGET 的使用 “陷阱”,起因是翻日志看到 Redis 请求报错日志:
(error) CROSSSLOT Keys in request don't hash to the same slot
定位到是 Redis 命令 MGET 抛出,简单说就是 MGET 在 Redis 集群模式下获取多个数据,如果这些 Key 没有哈希到同一个 Slot 就会报错。
那么怎么让需要批量获取的数据在一个 Slot 呢?使用 Hash Tag,先看未使用 Hash Tag 的错误示例:
127.0.0.1:7000> set app:article_meta:id-1 1
-> Redirected to slot [10828] located at 127.0.0.1:7001
OK
127.0.0.1:7001> set app:article_meta:id-2 2
OK
127.0.0.1:7001> set app:article_meta:id-3 3
-> Redirected to slot [2574] located at 127.0.0.1:7000
OK
127.0.0.1:7000> mget app:article_meta:id-1 app:article_meta:id-2 app:article_meta:id-3
(error) CROSSSLOT Keys in request don't hash to the same slot
复现出线上遇到的错误,而后在 Set 的时候使用 Hash Tag 试试。
127.0.0.1:7000> set app:{article_meta}:id-1 1
OK
127.0.0.1:7000> set app:{article_meta}:id-2 2
OK
127.0.0.1:7000> set app:{article_meta}:id-3 3
OK
127.0.0.1:7000> mget app:{article_meta}:id-1 app:{article_meta}:id-2 app:{article_meta}:id-3
1) "1"
2) "2"
3) "3"
使用 Hash Tag 标记 article_meta 后,这三个 Key 都会在一个 Slot 中。
问题很好解决,值得思考的是程序在设计缓存时,能否考虑到外部缓存服务的差异?当下的做法,我将这个问题添加到了 Code Review Skills 检查项中,避免再次被引入。
另外的信息:
Redis Cluster 默认使用完整 Key 计算 Hash Slot。当 Key 中包含 {...} 时,只会使用花括号中的内容计算 Slot。因此上例中的三个 Key 都使用 article_meta 计算 Slot,从而保证位于同一个 Slot。需要注意,Hash Tag 也意味着这些 Key 会集中到同一 Slot,在数据量较大的场景下需要关注热点和数据分布问题。