什么是分布式锁
为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些金进程调度.而这个分布式协调技术的核心就是来实现这个分布式锁.
为什么要使用分布式锁?
1.成员变量A存在JVM1,JVM2,JVM3三个JVM内存中
2.成员变量A同时都会在JVM分配一块内存,三个请求发过来同时对这个变量操作,显然结果是不对的
3.不是同时发过来的,三个请求分别操作三个JVM内存区域的数据,变量A之间不存在共享,也不具有可见性,处理的结果也是不对的
注:该成员变量A是一个有状态的对象
如果我们业务中确实存在这个场景的话,我们就需要一种方法解决这个问题,这就是分布式锁要解决的问题.
分布式锁应该具备的哪些条件?
1.在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行
2.高可用的获取锁与释放锁
3.具备可重入特性(可以理解为重新进入,由多于一个任务并使用,而不必担心数据错误)
4.具备锁失效机制,防止死锁
5.具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失效
分布式锁的实现有哪些?
1.memcached:利用memcached的add命令,此命令是原子性操作,只有在key不存在的情况下,才能add成功,也就意味着线程得到了锁.
2.Redis:和memcached的方式类似,利用Redis的setnx命令,此命令同样是原子性操作,只有在key不存在的情况下,才能set成功.
3.zookeeper:利用ZooKeeper的顺序临时截点,来实现分布式锁和等待队列.zookeeper设计的初衷,就是为了实现分布式锁服务的.
4.Chubby:Google公司实现的粗粒度分布式锁服务,底层利用了Paxos一致性算法.
通过Redis分布式锁的实现理解基本概念
分布式锁实现的三个核心要素:
加锁
最简单的方法是使用setnx命令.key是锁的唯一标识,按业务来决定命名,比如想要给一种商品的秒杀活动加锁,可以给key命名为"lock_state_商品ID".而value设计成什么呢?我们可以姑且设置成1,加锁的伪代码如下:
setnx(lock_state_商品ID,1)
当一个线程执行setnx返回1,说明key原本不存在,该线程成功得到了锁;当一个线程实行setnx返回0,说明key已经存在,该线程抢锁失败.
解锁
有加锁就得有解锁,当得到锁的线程执行完成任务,需要释放锁,以便其他线程可以进入,释放锁的最简单方式是执行del指令,伪代码如下:
del(lock_state_商品ID)
释放锁之后,其他线程就可以继续执行setnx命令来获得锁.
锁加时
锁超时是什么意思?如果一个得到锁的线程在执行任务的过程中挂掉,来不及显示地释放锁,这块资源将会永远被锁住(死锁),别的线程再也别想进来.所以,setnx的key必须设置一个超时时间,以保证即使没有被显示释放,这把锁也要在一定时间后自动释放.setnx不支持超时参数,所以需要额外的指令,伪代码如下:
expire(lock_state_商品ID,30)
致命性问题
1.非原子性操作
1.1 setnx
宕机
1.2 expire
解决方案
set(key,value,expire) redis2.6以上版本
2.误删锁
2.1 set(key,value,expire)
2.2数据没有操作完成,30秒,没有操作完成
2.3数据操作完成.del(商品ID) 删除的是JVM2的锁
解决方案
判断是不是自己的锁