Redis实现分布式锁

一 分布式锁的演进

使用setnx命令,在SpringBoot中为setIfAbsent,最简单的代码如下:

 

 当然上述代码会存在问题,如A服务在执行到中间时抛异常,则不能释放这个锁,加上try-catch-finally,如下:

 

 但是如果在中间的时候直接断掉,则仍然不能释放锁,加上锁的自动释放时间,如下:

 

 但是这个不能保证原子性,如果在正好执行到设置过期时间之前断掉,则仍然不能释放锁,放在一条执行语句里面,如下:

 

 但是在高并发情况下,线程一需要15秒,线程二需要8秒,线程一执行到第十秒会自动释放锁,这时线程二会拿到锁,线程二执行了五秒后,线程一也执行了五秒,这时一会执行finally里的代码释放锁,但是这个锁却是二的,这时线程三又会拿到这个锁去执行。。。如此下去会永久失效

解决问题的根本点:自己的锁被别人释放掉

解决方式:使用唯一id,只有是自己的锁才会自己去释放代码如下:

 

 但是这里仍然会有超时问题,设置短了太容易超时,设置长了不利于锁的释放。

使用从线程定时任务去执行监控主线程看看是否还在执行,如果是,则重新把超时时间延长,这也是一种解决方式。

二 Redission

2.1 代码

使用框架一步解决问题:

 2.2 原理

 实现原理如下图:

 里面的lock方法的最核心部分就是执行了一个lua脚本,保证了加锁和设置过期时间的原子性,原理如上图

2.3 仍然存在的问题

上图所示的redis是主从架构,当Master宕机但是仍然未同步到从节点的时候,从节点会选举出新的主节点,但是这是没有加锁的,这是因为Redission的分布式锁是满足AP的,即满足可用性,不满足一致性,只要主节点写了一个key,立马告诉redission客户端(不同于zookeeper的分布式锁是满足CP,zookeeper主节点获取key之后,超过半数的从节点同步之后才会返回,即满足一致性)

这个问题使用redis的Redlock可以解决(原理和zookeeper类似),但是牺牲了性能,并不推荐使用

posted @ 2021-03-06 17:01  Mistolte  阅读(120)  评论(0编辑  收藏  举报