赞
踩
最近,项目上线的时候,出现了一个Redis的报错:CROSSSLOT Keys in request don't hash to the same slot
,这个在内网环境下无法复现,因为正式环境的Redis是cluster集群模式,而我们内网环境是单机模式。(后面我在内网也部署了一个Redis集群,具体见我这一篇文章 《使用Docker搭建Redis Cluster集群》)
Redis的插槽(Slot
)是用于实现集群分片(Cluster Sharding
)的一种机制。Redis集群至少需要三个结点,每个结点处理一部分数据。那么怎样分配这些数据到各个结点呢?Redis Cluster 采用的是虚拟槽分区算法,其中提到了槽(Slot
)的概念。这个槽是用来存放缓存信息的单位,在 Redis 中将存储空间分成了 16384
(
2
14
{2}^{14}
214)个槽,也就是说 Redis Cluster 槽的范围是 0 -16383。
在存储数据的时候,集群会对 Key 进行 CRC16 校验并对 16384 取模:
slot = CRC16(key) % 16384
得到的结果就是 Key-Value 所放入的槽,从而实现自动分割数据到不同的节点上
为什么会是16384个插槽呢?Redis作者是这么说的:传送门
在集群模式下,所有涉及到多个 key
的Redis指令,都要求所有的 key
处于同一个 slot
,如果 slot
不同,哪怕实际上这些 slot
都在同一个结点上,也会报这个错:
CROSSSLOT Keys in request don't hash to the same slot
例如以下这些操作:
SUNIONSTORE destination key [key ...]
SDIFF key [key ...]
EVAL script numkeys key [key ...] arg [arg ...]
DEL key [key ...]
只要看到格式中有 key [key ...]
这样的操作,在集群中,都必须保证所有 key
在同一个 slot
。
禁止不同的slot操作,大概有以下原因:
既然Redis集群不允许跨slot的操作,那我们只要让key
强制分配到同一个Slot
就行了。
上面说了,正常情况下,slot = CRC16(key)%16384,这里是对整个key进行CRC16。
Redis提供了一种Hash Tag的功能,在key中使用{}
括起key中的一部分,在进行 CRC 16 (key) % 16384 的过程中,只会对{}
内的字符串计算。
例如,{rank:level}:1
,{rank:level}:2
这两个key就会分配都同一个slot,因为计算哈希的时候,都使用了 {}
中的那一部分:rank:level
,所以分配出来的 slot
就是一样的。
值得注意的是,这里的 {}
可以放在key的任意位置,例如 all{rank:level}
,all{rank:level}:1
也都是一样的。
另外,如果有多个 {}
的话,只有第一个 {}
生效,例如 {rank:level}:1:{2}
,用来计算哈希的只是第一个 {}
里面的 rank:level
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。