1.setnx key value // 设置成功返回1,设置失败返回0
2. do something ... // 进行逻辑操做
3.del key // 删除以前设置的key,即删除分布式锁
复制代码
初初级分布式锁使用setnx + del
完成,先使用setnx
设置key,若是设置成功,就开始进行逻辑操做,逻辑操做完成后,使用del
删除分布式锁。但若是在使用setnx
加锁后,因为某些缘由程序终止运行了,那么del
解锁命令就永远得不到执行,那么以前加的分布式锁就永远在redis中了,其余业务也就永远没法得到锁。redis
为了解决del
命令可能未执行致使锁永远没法释放的问题,因而使用到了expire
命令,给key加过时时间,防止del
命令未运行出现的死锁状况。bash
1.setnx key value // 设置成功返回1,设置失败返回0
2.expire key seconds // 为key设置过时时间
3. do something ... // 进行逻辑操做
4.del key // 删除以前设置的key,即删除分布式锁
复制代码
初级分布式锁使用setnx + expire + del
完成,首先setnx
设置key,若是设置成功则调用expire
为该key设置过时时间,而后开始作一些逻辑操做,最后相关逻辑操做完成后使用del
删除key来释放分布式锁。
初级分布式锁很清晰的逻辑,可是首先要明白,在setnx
、expire
两个命令之间也会由于某些缘由致使业务中断,程序就无法正常进行下去,也就会致使分布式锁失效。比方说: setnx
成功后,开始设置过时时间应该,可是这时候服务器宕机了,致使expire
命令未执行,因而,以前设置的key就会永远存在redis中,当服务器恢复正常后,正常业务就再也没法得到锁,由于他发现,这个key一直存在,再使用setnx
就一直失败。服务器
expire
命令未正常运行,这就和第一种“初初级分布式锁”仅用
setnx + del
状况是一毛同样了,出现了死锁。
1.set key value ex seconds nx // 设置成功返回1,设置失败返回nil
2. do something ... // 进行逻辑操做
3.del key // 删除以前设置的key,即删除分布式锁
复制代码
中级分布式锁的基础是redis版本已经升级到2.6.12版本以上,set
命令在该版本以前还仅是set key value
的操做模式,升级后就加了ex和nx等参数,使用ex和nx参数后等效于 setnx key value; expire key seconds
两条命令,且使用set
实现ex和nx的功能都是原子性的,不会出现ex和nx断档。分布式
该分布式锁虽然解决了setnx
和expire
两个命令的断档问题,可是若是do something
的逻辑操做执行时间过长会出现什么新的问题?lua
do something
时间超出设置的过时时间,若是这时候业务逻辑还没作完,业务B就来获取锁了,而后也会走do something
,这时候就会出现业务数据紊乱,也就打破了使用分布式锁的初衷do something
时间超出设置的过时时间,业务A的分布式锁被释放,业务B获取了该锁,正在执行,这时候业务Ado something
完成了,开始释放锁,结果把已经获取分布式锁的业务B的锁给释放了如何解决上述问题呢?spa
方法一:
针对问题二,能够在加锁操做时,给key的value设置一个随机数并记录下来,当do something
走完后,要释放锁时,先判断分布式锁的value和当前要删除的key的value,也就是以前设置的随机数是否一致,若是一致才能够删除,若是删除不了,就说明这个key已通过期了,被自动释放了。这个方法仍旧有一个弊端,就是在判断value是否一致时的操做也不是原子型的,假如业务A的锁还没过时,而后最后要开始释放锁,他先判断value是否一致,发现value是一致的,可是由于某些缘由致使要过N长时间才能执行del
操做,而后业务A的锁到期了自动释放了,而这时候业务B获取了锁,结果业务A的del
操做终于苏醒了,因而他开始释放锁,结果把业务B的锁给释放了,针对这个问题,说是能够用lua脚原本解决,由于 Lua 脚本能够保证连续多个指令的原子性执行(可是我目前还不会lua脚本)
方法二:
针对问题一的数据错乱,那目前没招... 要么就是在业务层作控制,比方说在do something
工做期间判断锁是否有效,无效的话就回滚当前业务,抛出异常,还比方说自动续期code