redis集群的多key原子性操作如何实现?
1、背景
在单实例redis中,我们知道多key原子性操作可以用lua脚本或者multi命令来实现。
比如说有一个双删场景,要保证原子性同时删除k1
和k2
。
可以用lua双删
EVAL "redis.call('del', KEYS[1]);redis.call('del', KEYS[2])" 2 k1 k2
也可以用事务双删
MULTI
del k1
del k2
EXEC
但是在redis的集群中,key被hash到不同的slot,slot又被分配到多个不同redis实例。那么多key原子性操作如何实现呢?
2、冲突
在redis集群中执行多key原子性操作,意味着要在不用redis实例之间执行多key原子性操作,要满足这个诉求,我们可以通过探索下面这个疑问,来挖掘正确解决方案。
参考单实例的思路,用lua脚本、multi能支持吗?
引申问题1:如果能,那么原理是怎样的?
引申问题2:如果不能,那么应该如何实现?
3、分析
参考腾讯云的解答
我们业务常用腾讯云,所以想到使用指南上一定有关于这个场景的方案。
关于集群版事务的描述
不建议使用事务
Redis 的事务功能较弱,不支持回滚,而且集群版本要求一次事务操作的 Key 必须在同一个 Slot 上。
这里腾讯云并没有说用了事务会如何?是会失效还是会报错?我觉得这个可以描述更清晰一些。读者知道可以评论回复。
关于集群版lua的描述
集群版使用 Lua 的特殊要求
所有 Key 都应该由 KEYS 数组来传递。redis.call/pcall 里面调用的 Redis 命令,Key 的位置必须是 KEYS array, 否则直接返回如下错误信息:error,“-ERR bad lua script for redis cluster,all the keys that the script uses should be passed using the KEYS array”
单个 Lua 脚本操作的 Key 必须在同一个节点上,否则直接返回如下错误信息:error, “-ERR eval/evalsha command keys must in same slotrn”
用lua脚本强调的是key必须都落到同一slot上
,这里并没有说怎么使key落到同一slot上
,所以我认为这个答案也并不完美。
总结一下,大致就是腾讯云不具备集群多实例间的多key原子性操作能力。
但是,可以通过将操作的多key都落到同一slot,利用单实例redis的能力来实现
。
参考redis官方的解答
经过上面的思考后,我想看看标准redis集群应该是怎么玩的?
关于"能力" 的描述
Redis Cluster implements all the single key commands available in the non-distributed version of Redis. Commands performing complex multi-key operations like set unions and intersections are implemented for cases where all of the keys involved in the operation hash to the same slot.
- redis 集群实现
所有
非集群版本的单key操作能力
- redis 集群
复杂的多key操作能力是基于所有key都落在同一slot的原则上实现的
问题一的思考,两个命令都属于#2,所以,官方也是建议多key原子性操作将操作的多key都落到同一slot,利用单实例redis的能力来实现
。
那么怎么使key落到同一slot上
?
关于多key落同一slot
Redis Cluster implements a concept called hash tags that can be used to force certain keys to be stored in the same hash slot.
redis集群提供了一个叫hash tags
的概念,利用这个能力,可以将多key都落到同一个slot中。
4、解决
解答疑问
综合上面的信息,我们可以解答我们的问题了。
参考单实例的思路,用lua脚本、multi能支持吗?
是,可以的。但是有一个前提,就是保证多key都落在同一个slot中。其原理是,将多key收拢在同一个slot中,并利用单实例redis的多key原子性操作能力。
使多key落在同一slot-hash tags
Hash tags
There is an exception for the computation of the hash slot that is used in order to implement hash tags. Hash tags are a way to ensure that multiple keys are allocated in the same hash slot. This is used in order to implement multi-key operations in Redis Cluster.
To implement hash tags, the hash slot for a key is computed in a slightly different way in certain conditions. If the key contains a “{…}” pattern only the substring between { and } is hashed in order to obtain the hash slot.
大概的意思就是,原来key落到slot的方式是根据key
进行hash的,如果我们给key起名的时候带上{x}
标志,那么进行hash的时候就会以x
作为hash key了。
用上述的例子,我们可将key名改成{k}1
,{k}2
,那么就可以使这两个key落在同一slot,就可以用起集群版的多key原子性操作能力了。
更好的使用方式
抛开redis集群自身的能力,应该还有更好的解决方案,比如分布式锁等这里就不再探讨,有更好的方式可以评论区交流。
参考:
腾讯云-命令使用准则
redis官方关于redis集群的描述