Redis CAS乐观锁实现

原创 2017年09月13日 15:59:27

随着业务量的增大,系统必然遇到了并发资源抢占的问题,也就引发了分布式锁的讨论。在实现了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时才具有持久性

狍狍的日常生活

版权声明:本文为博主原创文章,转载请注明出处。

相关文章推荐

AtomicInteger源码分析——基于CAS的乐观锁实现

AtomicInteger源码分析——基于CAS的乐观锁实现 1. 悲观锁与乐观锁         我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/pro...
  • a724888
  • a724888
  • 2017年03月30日 20:48
  • 175

乐观锁的一种实现方式——CAS

在深入理解乐观锁与悲观锁一文中我们介绍过锁。本文在这篇文章的基础上,深入分析一下乐观锁的实现机制,介绍什么是CAS、CAS的应用以及CAS存在的问题等。...

Java 多线程:AtomicInteger源码分析——基于CAS的乐观锁实现

AtomicInteger源码分析——基于CAS的乐观锁实现 1. 悲观锁与乐观锁         我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/p...

AtomicInteger源码分析——基于CAS的乐观锁实现

乐观锁 悲观锁 CAS java AtomicInteger concurrent
  • qfycc92
  • qfycc92
  • 2015年06月14日 10:15
  • 2362

[数据库事务与锁]详解八:底理解数据库事务乐观锁的一种实现方式——CAS

注明: 本文转载自http://www.hollischuang.com/archives/1537 在深入理解乐观锁与悲观锁一文中我们介绍过锁。本文在这篇文章的基础上,深入分析一下乐观...

Redis分布式锁----乐观锁的实现,以秒杀系统为例

本文使用redis来实现乐观锁,并以秒杀系统为实例来讲解整个过程。

redis中使用 check-and-set 操作实现乐观锁

redis中使用 check-and-set 操作实现乐观锁 转载2015-03-10 16:23:35 WATCH 命令可以为 Redis 事务提供 check-and-...

Java 多线程10:CAS 与 AtomicInteger(乐观锁)

AtomicInteger源码分析——基于CAS的乐观锁实现悲观锁与乐观锁我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,...

Java多线程 -- JUC包源码分析1 -- CAS/乐观锁

乐观锁思想 乐观锁的几种具体实现 Java CAS原子操作/AtomicInteger实现 ABA问题/AtomicStampedReference实现 Mysql 乐观锁实现 Mysql MVCC ...

java 5多线程乐观锁CAS之于悲观锁synchronized

在Java并发包中有这样一个包,java.util.concurrent.atomic,该包是对Java部分数据类型的原子封装,在原有数据类型的基础上,提供了原子性的操作方法,保证了线程安全。下面以A...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Redis CAS乐观锁实现
举报原因:
原因补充:

(最多只允许输入30个字)