实现方式
基于数据库实现,基于缓存redis实现,基于zookeeper实现
需要解决的问题
多进程可见:多进程可见是实现分布式锁的基础,redis天然支持
互斥:同一时刻,只能有一个进程可以获取锁,执行任务后释放,
可重入:同一进程再次获取锁不会被死锁
阻塞锁:获取失效时,具备重试机制,尝试再次获取锁
性能好:效率高,应对高并发场景
高可用:避免服务器宕机或者具有处理宕机的处理措施
失效考虑的问题
例如 setnx只有第一次能添加,即互斥,
设置过期时间以及finally中根据key释放锁来解决程序异常或者服务器宕机时的锁释放问题
如果setnx成功后还没来得及设置过期时间怎么办,需要确保setnx和设置过期时间的原子性:set lock 001 NX EX 100,利用nx和ex命令同时执行来保证原子性,ex 100值设置过期时间为100
A线程获取锁,但是执行过程中程序阻塞导致程序还没有执行完锁超时已被释放,B线程此时拿到了锁刚开始处理业务逻辑,A线程逻辑执行完成来释放锁,就会把B的锁给释放掉,C线程此时进来也可以拿到锁了,就会出现B,C线程安全问题
解决:添加锁时给锁做一个唯一标识,加锁的名字
可重如问题:加锁是记录一下获取到锁的线程是谁,第二次获取时先把线程的名字取出来,如果是已获取到的锁的线程又来获取锁,则直接放行
单锁重入出现嵌套获取锁时,不能在内部就把锁释放,应该在最外层释放,毕竟要记录线程名字时,还要记录锁重入了几成,没释放一次锁记录减一,知道最外层时才能del 锁的key
可以用redis的hash结构,key = lockkey ,value=map('线程名字','重入的层数')
实现以下这些命令的线程安全问题,保证原子性,需要用到LUA脚本,一次性执行。
阻塞特性的实现:for循环去获取锁,一次失败等一会再去获取,如果循环到达一定的总时长在抛获取锁失败,或者用更优的方式利用redis的发布订阅机制,释放锁有取发布一条消息,其他线程订阅到消息后在去尝试获取锁
高可用:主从同步未完成时,主宕机导致锁丢失,解决有一个redlock算法,多台机器都去获取锁,半数以上获取到才认为是成功获取,即使一台机器挂了还有几台,这样可以解决锁丢失的问题