Redisson分布式锁
约 974 字大约 3 分钟
2025-03-15
1.锁
- 锁:用来锁住资源,保证数据的安全性
- 本地锁:单个进程/机器内对共享资源的同步控制
2.分布式锁
定义:一种用于在分布式系统中控制多个节点对共享资源的访问机制,保证数据资源正确的被访问和修改。简单来说,就是用来确保在多个进程或者服务实例中,某些关键代码只能被一个进程或者服务实例执行,保证数据的一致性
分布式锁的条件:
- 互斥性:在任意时刻,只有一个节点能持有锁
- 超时机制:为防止出现死锁,需要设置锁的超时时间
- 自动续期:如果持有锁的节点处理任务的时间超过了锁的超时时间,出现线程没有处理完任务就释放锁的情况
实现分布式锁的方式:
- 数据库
- Zookeeper
- Redis
对比 Redis 和 Zookeeper
Redis分布式锁 | Zookeeper | |
---|---|---|
锁获取方式 | 需要不断尝试获取锁,比较消耗性能 | 获取不到锁时注册监听器,性能开销小 |
锁自动释放 | 客户端挂掉,需要等待超时时间释放锁 | 客户端挂掉后,临时节点自动删除,锁自动释放 |
应用场景 | 简单的分布式需求 | 高可用、一致性的分布式锁需求 |
实现原理 | Redis 键值对 | Zookeeper 临时节点 |
3.Redis分布式锁
3.1 实现原理
- 基于 set nx 命令实现
- 只有当指定的键名
key
不存在时,才成功将键值对存储到Redis数据库中。如果键名key
已经存在,则不执行任何操作。
SETNX key value
3.2 出现的问题
- 死锁:
- 问题:设置好了锁,但是服务出现宕机,没有执行删除锁逻辑,这就造成了死锁
- 解决:设置过期时间
- 删锁:
- 业务还没执行完锁就过期了,别人拿到锁,自己执行完去删了别人的锁
- 解决:删锁的时候明确是自己的锁,如 uuid 。或者逻辑业务还没执行完,锁续期(redisson有看门狗)
- 判断和删两个过程:
- 判断 uuid 对了,但是将要删除的时候锁过期了,别人设置了新值,那删除了别人的锁
- 删除锁必须保证原子性(保证判断和删锁是原子的)。使用redis+Lua脚本完成,脚本是原子的
3.3 存在的问题
- 复杂:原生 Redis 分布式锁需要开发者手动控制锁的获取、释放以及锁的过期时间管理
- 无自动续期:原生 Redis 没有自动续期的功能
- 可重入性:需要手动实现,思路:Redis 锁中的值记录线程信息,每次加锁记录加一,解锁记录减一
4.Redisson分布式锁
- 优势:
- 简单:封装了获取锁、删除锁的 API
- 支持自动续期:第一次加锁成功时会开启自动续期的定时任务,使用 Lua 脚本实现:判断当前线程对应的锁是否存在,若存在则重新设置锁的过期时间(默认为 30 秒)
- 可重入性:自动实现
5.项目业务
由于系统采用微服务架构,其中有两个服务:项目服务和流程服务。
在用户修改项目任务的审批设置时,会修改两个服务数据库的数据,分别是任务审批设置表和流程运行时数据表,分布式锁能确保状态变更的原子性。例如在更新 ACT_RU_EXECUTION 运行时数据表时,如果不加锁可能导致两个节点同时修改流程状态,造成状态机混乱。
