Redis之分布式锁简析

初初级分布式锁

1.setnx key value // 设置成功返回1,设置失败返回0
2.    do something ... // 进行逻辑操做
3.del key // 删除以前设置的key,即删除分布式锁
复制代码

初初级分布式锁使用setnx + del完成,先使用setnx设置key,若是设置成功,就开始进行逻辑操做,逻辑操做完成后,使用del删除分布式锁。但若是在使用setnx加锁后,因为某些缘由程序终止运行了,那么del解锁命令就永远得不到执行,那么以前加的分布式锁就永远在redis中了,其余业务也就永远没法得到锁。redis

如上图所示,非正常业务流程中,业务1得到锁后,最后del命令没有执行,致使后续其余业务加锁失败。

为了解决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来释放分布式锁。
初级分布式锁很清晰的逻辑,可是首先要明白,在setnxexpire两个命令之间也会由于某些缘由致使业务中断,程序就无法正常进行下去,也就会致使分布式锁失效。比方说: 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断档。分布式

该分布式锁虽然解决了setnxexpire两个命令的断档问题,可是若是do something的逻辑操做执行时间过长会出现什么新的问题?lua

  • 问题一:业务Ado something时间超出设置的过时时间,若是这时候业务逻辑还没作完,业务B就来获取锁了,而后也会走do something,这时候就会出现业务数据紊乱,也就打破了使用分布式锁的初衷
  • 问题二:业务Ado 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

相关文章
相关标签/搜索