Redis 锁的方式
setNx

SET my:lock 随机值 NX PX 30000,这个命令就ok,这个的NX的意思就是只有key不存在的时候才会设置成功,PX 30000的意思是30秒后锁自动释放。别人创建的时候如果发现已经有了就不能加锁了。
释放锁就是删除key,但是一般可以用lua脚本删除,判断value一样才删除:
关于redis如何执行lua脚本,自行百度
if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])elsereturn 0end
为啥要用随机值呢?因为如果某个客户端获取到了锁,但是阻塞了很长时间才执行完,此时可能已经自动释放锁了,此时可能别的客户端已经获取到了这个锁,要是你这个时候直接删除key的话会有问题,所以得用随机值加上面的lua脚本来释放锁。
但是这样是肯定不行的。因为如果是普通的redis单实例,那就是单点故障。或者是redis普通主从,那redis主从异步复制,如果主节点挂了,key还没同步到从节点,此时从节点切换为主节点,别人就会拿到锁。
RedLock

这个场景是假设有一个redis cluster,有5个redis master实例。然后执行如下步骤获取一把锁:
1)获取当前时间戳,单位是毫秒
2)跟上面类似,轮流尝试在每个master节点上创建锁,过期时间较短,一般就几十毫秒
3)尝试在大多数节点上建立一个锁,比如5个节点就要求是3个节点(n / 2 +1)
4)客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了
5)要是锁建立失败了,那么就依次删除这个锁
6)只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁
redis分布式锁的问题
第一种方案,基于redis单实例,不靠谱,因为redis单点故障,会导致系统全盘崩溃,做不到高可用,除非你是那种不太核心的小系统,随便用一下分布式锁,你可以这么弄,应该还好,毕竟对高可用要求没那么高
第二种方案,基于redis主从架构+哨兵,保证高可用,master宕机,slave接替,但是有隐患,master宕机的一瞬间,还没异步复制锁到slave,导致重复加锁的问题,高可用是高可用了,但是锁的实现有漏洞,可能导致系统异常
客户端刚在master实例加了一个锁,但是master->slave的复制数据(锁复制过去)是异步的,导致master突然宕机,此时锁还没复制到slave,然后master->slave主备切换(哨兵),客户端B此时也对同一个key上锁,此时就会成功的在切换为master的slave实例上加锁,客户端A和客户端B同时对一个key完成了上锁
只要多个客户端同时对某个key上锁,就会导致数据肯定会出错
第三种方案,基于redis多master集群(redis-cluster,或者redis部署多机器,twitter开源的twemproxy做客户端集群分片,豌豆荚开源的codis做集群分片,都行),redlock算法,个人不推荐,实现过程太复杂繁琐,很脆弱,多节点同时设置分布式锁,但是失效时间都不一样,随着不同的linux机器的时间不同步,以及各种你无法考虑到的问题,很可能出现重复加锁
举个例子,给5个redis master都设置了一把锁key,失效时间是10s,但是因为设置key的时间可能因为网络等各种情况不同,会导致,也许客户端A还加着锁呢,因为处理耗时特别长,然后结果过了几秒钟,其中3台机器的锁key都过期失效了,因为这3台机器加锁的时间都比较靠前,已经10秒过期了,此时分布式锁自然失效,客户端B成功加锁,出现两个客户端同时加锁的问题
所以redlock算法,有两个问题,第一是实现过程和步骤太复杂,上锁的过程和机制很重,很复杂,导致很脆弱,各种意想不到的情况都可能发生;第二是并不健壮,不一定能完全实现健壮的分布式锁的语义
http://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html
这是国外一个牛人写的redlock算法的分析小论文,大家可以看一下,里面的结论就是我时候的那两个,第一是太复杂,第二是不健壮可能加锁会失败
当然redis作者不赞同他,但是同样redis作者自己也没法证明他的算法是健壮的,所以这个方案不建议大家采用
因为redis RedLock分布式锁的算法,涉及过程比较复杂,涉及到很多不同情况下的时间问题,就会导致说,你如果要证明这个算法是ok的,到最后其实会成为一个数学问题,你要从数学和逻辑的角度,大量的推算,证明说,这个算法,无论在任何极端的情况下都是成功的
综上所述,redis分布式锁实际上目前没有100%完美的方案,或多或少有点问题,实际生产系统中,我设计的系统有时候用zk分布式锁,但是有的时候也会用redis分布式锁。
