Redis CAS乐观锁实现

Redis CAS乐观锁实现

 版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/luqiang81191293/article/details/77967275

随着业务量的增大,系统必然遇到了并发资源抢占的问题,也就引发了分布式锁的讨论。在实现了ZK锁后,虽然解决了部分问题,但总感觉还有更好的方法(Redis锁性能肯定是比ZK高的,在这里就不讨论了)。所以借助于CAS理论和Redis实现无锁并发的念想就慢慢滋生了。顺便读了下Redis官方文档和Redis设计与实现发现Redis已经实现了CAS的操作,也就是我们所说的伪事物。

现状

先说下目前业务现状,目前有个动态派工师傅资源抢占的问题。目前使用的是ZK锁加数据库操作,凸显的问题就是业务漏斗式筛选时间过长,导致数据库锁等待增多。在高并发下这简直就是致命的问题。

优化方案

1、将师傅数据从目前的数据库操作转移到Redis缓存操作,目的减少数据库压力,解决锁等待资源占用问题。 
2、使用乐观锁替代目前的ZK锁,提高单体并发能力。

实现方案

乐观锁实现使用Redis 自有的watch multi exec等命令进行封装 
总体就是一句话概括,使用了相关命令就实现了CAS操作

下面详细介绍下Redis的CAS方案 
说起Redis的CAS就必须要说到它的事物了

Redis事物 
Redis 通过MULTI 、EXEC、WATCH等命令来实现事物功能。事物提供了一种将多个命令请求打包,然后一次性、按顺序的执行多个命令的机制,并且在事物执行期间,服务器不会中断事物而去执行其他客户端的命令请求,它会将事物中所有的命令都执行完毕。Redis事物不支持回滚操作,所以事物队列中某个命令执行错误,整个事物也会继续执行下去。

事物阶段 
- 开始事物 
MULTI 代表将客户端的REDIS__MULTI选项打开,使客户端进入事物状态执行命令返回QUEUED 代表命令已经进入事物队列了,EXEC命令将这个事物提交给服务器执行. 
- 命令入队 
当Redis处理一个请求时, 其实并不是所有的命令都会被放进事务队列, 其中的例外就是 EXEC 、 DISCARD 、 MULTI 和 WATCH 这四个命令 。当这四个命令从客户端发送到服务器 时, 它们会像客户端处于非事务状态一样, 直接被服务器执行。每个Redis客户端都有自己的事物状态,这个事物状态保存在客户端状态的mstate属性里面。

流转图
- 提交事务 
一个处于事物状态的客户端向服务器发送EXEC命令时,这个EXEC命令将立即被服务器执行。服务器会遍历这个客户端的事物队列,执行队列保存的所有命令,最后统一返回所有的执行结果。 执行是有序的按照先进先出(FIFO)的顺序执行机制。事务在执行过程中不会被中断,所有命令命令执行之后,事务才结束。

WATCH命令实现 
WATCH命令就是一个乐观锁,它可以在EXEC命令执行之前,监视任意数量的数据库键,并在EXEC命令执行时,检查被监视的键是否被修改了,如果被修改了服务器将拒绝执行事物,并向客户端返回空。

事物执行

通过此图会发现事物期间,其他客户端如果执行了相同的key操作,将会被忽略

CAS执行 
T4时间客户端二修改了testkey值,当T6阶段客户端一执行EXEC命令时,服务器会发现WATCH的键已经被修改,因此服务器拒绝执行客户端A的事物,并返回空。

WATCH监控 
每个Redis数据库都保存着一个watched_keys字典来保存被监控的健,字典值是一个链表,链表中记录了所有监视相应键的客户端

watched_keys字典

监控机制触发 
所有对数据库进行修改的命令,如SET、LPUSH、SADD、ZREM、DEL、FLUSHDB等,在执行之后都会调用multi.c/touchWatchKey函数对watched_keys字典进行检查,查看是否有客户端正在监视刚刚被命令修改过的key,有的话touchWatchKey函数会将监视的客户端的REDIS_DIRTY_CAS标识打开,表示该客户端事物安全性已经被破坏。

判断事物是否安全 
当服务器接收到一个客户端发来的EXEC命令时,服务器将会根据这个客户端是否打开REDIS_DIRTY_CAS标识来决定是否执行事物

执行流程

总结 
- 事物提供了一种将多个命令打包,然后一次性有序(FIFO)执行的机制 
- 事物执行过程不会被中断 
- 带WATCH命令的事物会将客户端和被监视的键在数据库watched_keys字典中进行关联,当键被修改程序会将所有监视键的客户端REDIS_DIRTY_CAS标识打开 
- 在客户端提交EXEC命令时,会检查REDIS_DIRTY_CAS标识,标识打开事物将不会被执行 
- Redis事物具有ACID的特性(当服务器运行在AOF模式下,并且appendfsync选项值为always时才具有持久性

猜你喜欢

转载自blog.csdn.net/u010412301/article/details/89361106