先上结果:
$redis->sDiffStore('live_room:robots:data:' . $info['id'], 'user_info:robots_list', '');
上述代码执行后redis抛出一个异常。来看redis源码是如何抛出这个异常的(附redis源码地址:redis/cluster.c at 5.0 · redis/redis · GitHub):
CLUSTER_REDIR_CROSS_SLOT 这个异常,继续定位:
这里已经说的很明白了,如果一个redis请求操作了多个Key,并且key不在同一个slot下,就会出现这个异常,来看源码的实现:
问题很好定位,解决的话我是放弃了redis提供的方法,自己将key1的内容拿出来,利用协程去将这些数据跑到新key下,此举在数据量大的情况下是否合适另说,紧急情况下解决问题为主。
通过这个问题明确几个概念:
1. redis 集群的 slot :
redis 集群中的 key 空间被划分为 16384 个 hash slot,节点数量也被限制为最多16384个(建议1000以内)。每个节点负责一部分的 hash slot,key 划分到 hash slot的规则如下:
HASH_SLOT = CRC16(key) mod 16384
hash slot 最大的作用应该就是提高扩展性了,可以很好地帮助集群的横向伸缩,增减节点时只需要将节点上的 slot 转移到其他节点。
2. 哪些操作会被限制:
集群模式,一次请求,涉及多个key。
- 多 key 命令:mset mget sDiffStore sUnionStore 等
- 多 key 的事务(MULTI)
- 多 key 的Lua脚本
3. 出现环境:
据说!是据说啊!!!我没有证据。
Redis Enterprise 没有这个问题,只是开源版有这个问题
4. 解决办法:
hash tag
-------------------------------------------------------虚线以内部分为摘录
hash tag 可以影响 hash slot 的生成,相同 hash tag 的 key 会被分配到相同的 hash slot。hash tag使用 {...}
形式。对于包含 hash tag 的 key,redis只会对 {}
内的字符串计算 hash,从而相同 hash tag 的 key 会计算得到相同的 hash slot。
一个有效的 hash tag 应该是key中首个 {
和首个 }
(在首个 {
之后) 之间有字符存在。
示例:
{user1000}.following
、{user1000}.follower
有相同 hash tag,会被分到相同的 slotfoo{}{bar}
没有有效的 hash tag,会按照foo{}{bar}
进行 hash 计算foozap
有有效的 hash tag,会按照{bar
进行 hash 计算foo{bar}{zap}
有有效的 hash tag,会按照bar
进行 hash 计算
注意,hash tag 要合理使用,避免大量的 key 被分配到相同 slot 里导致数据存储和访问倾斜。
-------------------------------------------------------虚线以内部分为摘录
可见,hash tag可以使那些key中有相同且固定字符串的key最终被分配到同一个hash slot中,这样从数据源头上避免了跨slot操作的问题,但是不能滥用,并且需要在一开始的代码结构设计中就要考虑到这个问题,不然就像我上述出问题的代码,多个key是完全不同的,在不变key的情况下,即使有hash tag也不能解决这个问题,只能暂时用笨办法一个一个循环插入了。
总结完毕 收工结束!