Redisson分布式锁

使用

两个常用API

tryLock:非阻塞锁,如果不设置waitTime的话,就直接return了

lock:阻塞锁,线程获取不到锁,会阻塞住

为什么基于lua脚本实现而不是事务?

两者都是可以保证原子性的,但

可重入锁?

redisson是基于redis的数据结构hash去实现可重入锁的。key为lock,field为线程名,value为count。这个

count就是用来表示一个锁被同一线程持有的情况

加锁流程:就是会先判断锁是否被当前线程持有或者说有没有线程持有,不是就表示加锁失败,是的话,就

count++;

解锁流程:每次执行完毕就count–。直到count==0,就把其key删除掉,表示锁完全释放

加锁流程和解锁流程都是基于lua脚本实现的

不止hash结构?

1.字符串拼接,把count拼进去

2.redis那里还是使用string,服务内部通过concurrentHashMap保存:key为线程唯一标识,value为锁

计数器

1
2
3
4
5
6
// 用于保存线程对应的锁和重入次数
//外层 String 是线程 ID(可以用 Thread.currentThread().getId() 拼上业务 ID),或 UUID。
//内层 Map:key 是 Redis 的锁 key,value 是重入次数。
private final ConcurrentHashMap<String, Map<String, Integer>> threadLockMap
= new ConcurrentHashMap<>();

可阻塞锁,可重试锁?

Redisson的可阻塞锁是基于Redis的发布订阅模式来实现等待,唤醒,获取锁失败的重试机制的

获取锁失败不是直接重试or返回,而是订阅一下,然后等待

当获取锁成功的线程释放锁后,就会发布一条消息

其他线程就会收到这条消息,从而重新获取锁,获取失败就会继续等待

但也不是无限等待,超过一定时间,就不会继续等了,而是会返回false(针对于tryLock())

联锁?

对于分布式锁在主从架构中的锁丢失问题,Redisson提供了一种联锁机制。它要求Redis使用多主多从或者多

主,那么只有所有Redis主节点都上锁成功,才算上锁成功。这样的话,如果某个主节点宕机了,那么其他主

节点也是有锁的数据的,新线程想要给所有主节点加锁,那还是会加锁失败的

ps:主从架构中的锁丢失问题:当主节点setnx成功后,这时候来没来得及同步就挂了。那么这时候就会重新

选主,但这时候新的主节点是没锁数据的,服务器就会认为锁已经释放了,导致锁的互斥性失效了。

红锁?

但对于联锁而言,还可能会存在以下问题

1.所有主节点都得上锁,若某个主节点由于网络原因,导致加锁时间长,加锁失败

2.或者某个主节点宕机了,一直加锁失败

那么如果出现以上情况,就会导致一直加锁失败,失败概率很高,而且加锁失败后是要回滚所有Redis主节点

的数据的,性能很差的。

那么基于以上问题………………

Redis官方提供了一种红锁机制,也要求Redis要多主部署,但加锁时只要半数以上加锁成功就OK了。如果

当前线程加锁加到半数以上,那么其他线程就不可能加锁加到了半数以上了,那么这样就满足了互斥性

但redLock也是有很大问题的,

主要原因是Redis创始人不推荐在严格一致性的分布式情况下使用它

有什么可以替代的吗?业界无公认的方案

我个人的思考

1.使用单实例的Redis锁,虽然会有单节点故障

2.业务做好唯一性校验

3.使用强一致性组件来实现分布式锁,如zookeeper

分布式锁检验死锁?

观察看门狗线程,如果出现某两个看门狗线程存活时间过长,则这两个看门狗对应的分布式锁可能产生死锁