参考链接
文章目录
- 分布式锁的作用
- 分布式锁应该具有的条件
- 实现1 基于数据库
- 实现2 基于 Redis
- 实现3 基于 ZooKeeper
分布式锁的作用
-
分布式锁是一种 跨机器的互斥机制 来控制 共享资源的访问
-
具体地,为了保证一个方法或属性在高并发情况下的同一时间只能被同一个线程执行,在单机部署的情况下,可以使用并发处理相关的功能进行互斥控制。随着业务发展,单机部署的系统演化成分布式集群系统后,由于分布式系统多线程、多进程并且 分布在不同机器上,使原单机部署情况下的并发控制锁策略失效,需要分布式锁解决此问题
分布式锁应该具有的条件
- 在分布式系统环境下,一个方法(资源)在同一时间只能被一个机器的一个线程执行(访问)
- 高可用的获取锁与释放锁
- 高性能的获取锁与释放锁
- 具备可重入特性
- 具备锁失效机制,防止死锁
实现1 基于数据库
-
核心思想
在数据库中创建一个表,表中包含方法名等字段,并在该字段上创建唯一索引,想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁 -
存在弊端
- 数据库的可用性和性能将直接影响分布式锁的可用性及性能
- 不具备可重入的特性,因为同一个线程在释放锁之前,行数据一直存在,无法再次成功插入数据。改进方案是在表中新增一列,用于记录当前获取到锁的机器和线程信息,在再次获取锁的时候,先查询表中机器和线程信息是否和当前机器和线程相同,若相同则直接获取锁
- 没有锁失效机制,有可能出现成功插入数据后,服务器宕机,对应的数据没有被删除,当服务恢复后一直获取不到锁。改进方案是在表中新增一列,用于记录失效时间,并且需要有定时任务清除这些失效的数据
实现2 基于 Redis
- 使用命令
命令 | 作用 |
---|---|
setnx key val | key 不存在时插入(key, val),返回1;否则不插入,返回0 |
expire key timeout | 设置 key 的过期时间,单位为秒 |
delete key | 删除 key |
- 实现
- 获取锁的时候,使用
setnx
加锁,并使用expire
命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的 value 值为一个随机生成的UUID,用于锁的释放 - 给获取锁的线程设置一个 获取的超时时间,若超过这个时间则放弃获取锁,防止线程持续尝试获取锁
- 释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放
- 获取锁的时候,使用
实现3 基于 ZooKeeper
-
ZooKeeper 是一个为分布式应用提供一致性服务的开源组件,内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名
-
实现
- 创建一个目录 mylock
- 线程A想要获取锁,在 mylock 目录下创建临时顺序节点
- 获取 mylock 目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁
- 线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点
- 线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁