分布式锁是一种用在分布式系统中实现同步和互斥访问的机制。
1、分布式锁概念
满足分布式系统或者集群模式下,多进程可见并且互斥的锁。
分布式锁的核心思想就是让分布式集群中的节点都适用同一把锁,只要大家使用的是同一把锁,就能锁住线程,让程序串行执行。
分布式锁需要满足的条件:
- 可见性:多个线程都能看到相同的结果 --- > 不是并发编程中的内存可见性,而是多个进程之间能够互相感知变化
- 互斥:分布式锁的基本条件,是程序串行执行
- 高可用
- 高性能
- 安全性
常见的分布式锁:
- mysql:mysql本身就带有锁机制,但是因为性能一般,所有不常用做分布式锁
- redis:利用setnx这种互斥命令就可以简单实现分布式锁
- zookeeper:利用节点的唯一性和有序性实现互斥。
2、redis实现分布式锁的核心思路
利用redis的set nx 方法,多个线程进入时,只有第一个线程能够 setnx key 成功,成功则说明他抢到了锁,那么就可以继续执行业务,执行完毕删除锁,没有成功的则等待一定时间重试。
使用set nx 加锁的时候,一定要注意加上过期时间,否则,一旦获得锁的进程宕机,那么锁就永远无法释放,其他进程也就没办法获取锁了。
3、业务处理时间较长会出现什么情况?
场景:
- 线程1获取锁,然后处理业务,但是因为业务处理时间较长,直到锁过期了还没处理完,此时线程2获取到了锁,然后处理线程2的业务
- 一段时间后,线程1处理完了,处理释放锁的逻辑,但是此时是线程2持有锁,他就把线程2的锁给释放了 --- 这就会产生问题
解决方法:
- 加长锁的过期时间,并添加一个子线程,每10s去确认当前持有锁的线程是否在线,如果在线,就将过期时间重设延长
- 给锁加一个唯一的UUID,保证每把锁的key是和自己的进程是绑定的
我们开发中,可以直接用Redisson来解决上述的问题,我们常说的watch dog就是上述所说的确认当前线程是否存活的子线程。
4、如果redis采用的是主从集群模式,会有什么样的问题?
因为redis采用的是AP模式,也就是保证高可用和高性能,而不是去保证高一致性,加锁时只会往master节点设置锁,并直接返回结果(此时如果master节点掉线,锁还没有同步给slave节点,而redis的选举机制会重新推选一个新的master,但是这个新的master没有同步到刚刚加的锁,此时就会发送线程不安全问题)
Redisson的解决方案: RedLock
RedLock的逻辑是,需要把锁加锁的逻辑同步到集群中的所有节点上(master和slave),如果大多数的redis节点(>n/2+1)成功获取到了锁,并且获取锁的总消耗时间没有超过锁的有效时间,才会被认为成功获取锁,否则认为获取锁失败,如果获取锁失败了,那么客户端应该立即向所有的redis节点发起释放锁的操作,用LUA脚本。