分布式锁的实现之 redis 篇
为什么需要分布式锁引入经典的秒杀情景,100件商品供客户抢。如果是单机版的话,我们使用synchronized 或者 lock 都可以实现线程安全。但是如果多个服务器的话,synchronized 和 lock 就不管用了(废话,怎么可能管用,都不在同一段代码了)。 分布式锁就是被设计出来实现多个服务器的线程安全。 很容易想到的方案是把共享变量(锁)抽取出来放在一个公共的数据库里(Redis、Memchhed)里,所有的服务器通过这个公共的资源实现数据的一致性,防止超卖。 具体实现分布式锁的实现方式有:Memchched分布式锁、Redis分布式锁、Zookeeper分布式锁,这里我们以Redis分布式锁为例,Redis分布式锁也是现在使用得最多的 1. 思路
2. 第一个问题:锁无法被释放试想一下,如果你执行完set命令服务器宕机了,来不及del解锁,那么这个锁永远无法被释放,其他线程无法执行。 解决方法是key必须设置一个超时时间,即使没有被显示释放,也在超时后自动释放。 redis为我们提供了这个命令设置超时时间
因此加锁的操作变成:
但是这两个操作不保证原子性(Redis单条操作保证原子性),如果加完锁还没设置过期时间服务器就宕机了,同样会导致死锁,因此加锁整个操作必须保证原子性。 redis提供了set+过期时间的原子操作
3. 第二个问题:错误释放锁第二个问题,如果线程执行时间超过TTL,当前锁被自动销毁 但是等线程执行完了,原来的del方法还会执行,它就会去执行解锁操作,把其他线程占用的锁给del了,这会产生非常严重的问题
解决方案是key的value不再是默认的了
这样写其实还有个问题,判断和删除无法保证原子性,还是有可能误删。因此解锁我们使用lua脚本来保证原子性:工具类有实现lua脚本的方法。
(解锁操作也可用事务来保证原子性,应付面试,实战还是lua脚本) 4. 第三个问题:超时解锁导致并发加锁和解锁操作我们都搞定了,但是还有一个问题:如果你的线程执行时间超过ttl过期时间,锁还是被释放了,其他线程可以和次线程并发执行,这是我们并不想看到的。 因此我们要为ttl延时 我们可以让获得锁的线程开启一个守护线程,用来给快要过期的锁“续航”。 5. 集群环境下可能出现的问题redis集群环境,多个master,多个slave的情况下: 当主节点挂掉时,从节点会取而代之,但客户端无明显感知。当客户端 A 成功加锁,指令还未同步,此时主节点挂掉,从节点提升为主节点,新的主节点没有锁的数据,当客户端 B 加锁时就会成功。 也就是主结点加了锁就宕机了,从节点还没同步,当该从节点提升为主节点时就会出错。 解决方案我也不清楚....以后碰到再找资料 开源框架Redisson上面的流程如果手写的话会要人老命,开源框架Redisson帮我们摆平一切,现在用得十分多 直接上代码:
结语分布式锁看起来难其实原理还是很简单的,没事多看看官方文档,讲得挺细致的 参考
(编辑:北几岛) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |