基于redis的乐观锁实践

redis真是一个分布式应用场景下的好东西,对于我们的应用设计,功劳大大的!

今天要研究的是基于redis的事务机制以及watch指令(CAS)实现乐观锁的过程。

所谓乐观锁,就是利用版本号比较机制,只是在读数据的时候,将读到的数据的版本号一起读出来,当对数据的操作结束后,准备写数据的时候,再进行一次数据版本号的比较,若版本号没有变化,即认为数据是一致的,没有更改,可以直接写入,若版本号有变化,则认为数据被更新,不能写入,防止脏写。

下面,看看如何基于redis实现乐观锁。

首先,看看redis的事务,涉及到的指令,主要有multi,exec,discard。而实现乐观锁的指令,在事务基础上,主要是watch指令,以及unwatch指令,unwatch通常可以不用!

案例1:redis的纯事务

下面是ssh窗口1里面的操作:

127.0.0.1:6379> set hello 1
OK
127.0.0.1:6379> get hello
"1"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr hello
QUEUED
127.0.0.1:6379> incr hello                 #这一步执行完毕后,去另外一个窗口(ssh窗口2),对hello这个key做incr操作,将hello对应的值变成2。完成后,继续后面的exec指令
QUEUED
127.0.0.1:6379> exec
1) (integer) 3                             #注意,这时hello的值是3了,前面执行get hello指令时,值是1哟,说明这个值在其他地方被修改过,这里的其他地方,就是指前面提到的,在另外一个连接窗口里面执行的。
2) (integer) 4
127.0.0.1:6379>

这个情景下,multi和exec之间的指令,依然是可以执行的。

下面的操作,就是在ssh窗口2里面的操作:

127.0.0.1:6379> 
127.0.0.1:6379> get hello
"1"
127.0.0.1:6379> incr hello
(integer) 2
127.0.0.1:6379>

案例2: 利用watch指令,基于CAS机制,简单的乐观锁

下面是ssh窗口1里面的操作:

127.0.0.1:6379> watch hello
OK
127.0.0.1:6379> get hello
"4"
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr hello
QUEUED
127.0.0.1:6379> incr hello                #这一步执行完毕后,去另外一个窗口(ssh窗口2),对hello这个key做incr操作,将其值变成5。完成后,继续后面的exec指令
QUEUED
127.0.0.1:6379> exec
(nil)                                     #注意,这是exec执行后返回的是nil,表示事务提交执行失败
127.0.0.1:6379> 
127.0.0.1:6379> get hello                 #这个时候,查看hello对应的值,就是在另外一个窗口(ssh窗口2)执行incr后的值
"5"

下面是ssh窗口2里面的操作:

127.0.0.1:6379> incr hello
(integer) 5
127.0.0.1:6379> 

案例3:watch指令在一次事务执行完毕后,即结束其生命周期

下面是ssh窗口1里面的操作:

127.0.0.1:6379> multi                     #接着上面案例2后,不再输入watch hello这个指令,直接启动事务
OK
127.0.0.1:6379> incr hello
QUEUED
127.0.0.1:6379> incr hello                #这一步执行完毕后,就在另外一个窗口(ssh窗口2),执行incr hello,将hello的值变成6。
QUEUED
127.0.0.1:6379> exec                      #另外一个窗口(ssh窗口2)里面的操作结束后,继续来这个窗口执行该指令,依然完成了上面的两个incr hello的操作。
1) (integer) 7
2) (integer) 8
127.0.0.1:6379>

下面是ssh窗口2里面的操作:

127.0.0.1:6379> incr hello
(integer) 6
127.0.0.1:6379>

上述3个案例的操作,指令其实非常的少,两个窗口的指令全集,截图如下:

在另外一个窗口(ssh窗口2)中的操作:

通过这个简单的例子,基于redis的乐观锁,可以得出一个结论:

1. 乐观锁的实现,必须基于WATCH,然后利用redis的事务。

2. WATCH生命周期,只是和事务关联的,一个事务执行完毕,相应的watch的生命周期即结束。

原文地址:https://www.cnblogs.com/shihuc/p/7284986.html