Redis事务机制和分布式锁

Redis事务机制

严格意义来说,Redis的事务和咱们理解的传统数据库(如mysql)的事务是不同的;Redis的事务实质上是命令的集合,在一个事务中要么全部命令都被执行,要么全部事物都不执行。
一个事务从开始到执行会经历如下三个阶段:java

  1. 开始事务。
  2. 命令入队。
  3. 执行事务。

在MySQL中咱们使用START TRANSACTION 或 BEGIN开启一个事务,使用COMMIT提交一个事务;而在Redis中咱们使用MULTI 开始一个事务,由 EXEC 命令触发事务, 一并执行事务中的全部命令。mysql

这里写图片描述

能够看到,MULTI 开始到 EXEC结束前,中间全部的命令都被加入到一个命令队列中;当执行 EXEC命令后,将QUEUE中全部的命令执行。redis

此外咱们可使用DISCARD取消事务。sql

这里写图片描述

须要注意的是:
1.Redis的事务没有关系数据库事务提供的回滚(rollback),因此开发者必须在事务执行失败后进行后续的处理;
2.若是在一个事务中的命令出现错误,那么全部的命令都不会执行
3.若是在一个事务中出现运行错误,那么正确的命令会被执行数据库

WATCH并发

研究过java的J.U.C包的人应该都知道CAS,CAS是一种保证原子性的操做。那么redis也提供了这样的一个机制,就是利用watch命令来实现的。分布式

WATCH命令能够监控一个或多个键,一旦其中有一个键被修改(或删除),以后的事务就不会执行,监控一直持续到EXEC命令。工具

分布式锁

什么是分布式锁?spa

分布式锁是控制分布式系统之间同步访问共享资源的一种方式。若是不一样的系统或是同一个系统的不一样主机之间共享了一个或一组资源,那么访问这些资源的时候,每每须要互斥来防止彼此干扰来保证一致性,在这种状况下,便须要使用到分布式锁。线程

实现分布式锁有不少实现方式和工具,如Zookeeper、Redis等。

使用Redis实现分布式锁原理:

Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的链接并不存在竞争关系,基于此,Redis中可使用SETNX命令实现分布式锁。

SETNX——SET if Not eXists(若是不存在,则设置):

setnx key value

将 key 的值设为 value ,当且仅当 key 不存在
若给定的 key 已经存在,则 SETNX 不作任何动做。

这里写图片描述

若是须要解锁,使用 del key 命令就能释放锁:

这里写图片描述

左图首先使用setnx对键加锁成功返回1,右图再次使用setnx命令对键加锁失败返回0,说明有客户端持有锁。使用del释放锁之后,右图就可使用setnx命令对键加锁。

解决死锁

若是一个持有锁的客户端失败或崩溃了不能释放锁,该怎么解决?

答:给锁设置一个过时时间,能够经过两种方法实现:经过命令 “setnx 键名 过时时间 “;或者经过设置锁的expire时间,让Redis去删除锁。

第一种实现方式:
使用 setnx key “当前系统时间+锁持有的时间”和getset key “当前系统时间+锁持有的时间”组合的命令就能够实现。
具体作法以下:

客户端2发送SETNX lock.test 想要得到锁,因为以前的客户端1还持有锁,因此Redis返回一个0
客户端2发送GET lock.test 以检查锁是否超时了,若是没超时,则等待或重试。
反之,若是已超时,客户端2经过下面的操做来尝试得到锁:
GETSET lock.test 过时的时间
经过GETSET,客户端2拿到的时间戳若是仍然是超时的,那就说明,客户端2如愿以偿拿到锁了。
若是在客户端2以前,有个客户端3比客户端2快一步执行了上面的操做,那么客户端2拿到的时间戳是个未超时的值,这时,说明客户端2没有如期得到锁,须要再次等待或重试。
尽管客户端2没拿到锁,但它改写了客户端3设置的锁的超时值,不过这一点很是微小的偏差带来的影响能够忽略不计。

第二种就很是简单了:
经过Redis中expire()给锁设定最大持有时间,若是超过,则Redis来帮咱们释放锁。

1.客户端1使用setnx得到了锁,而且使用expire设定一个过时时间,假定是10ms

2.过了4ms后,客户端1不幸运的宕机了,此时客户端2想要经过setnx尝试得到锁,可是锁尚未过时,任然被客户端1所持有。

3.到了11ms时,锁过时了,Redis帮咱们删除了锁,此时客户端2想要经过setnx尝试得到锁,此时就能成功得到锁。

在实际过程当中,咱们能够设定一个时间T,用来表示客户端在初次尝试得到锁失败之后,在屡次尝试得到锁所花的时间。若是次时间为0,表示除此尝试得到锁失败之后就不会再去尝试得到锁了。

相关文章
相关标签/搜索