首页
分布式锁
分布式锁的意义在于提供一个高可用的分布式场景下的互斥机制,任何方案都不是绝对可靠的,分布式系统的NPC问题(网络延迟、进程暂停、时钟漂移)均可能导致执行过程中锁意外释放,故需要额外的兜底措施(如乐观锁)以保证正确性。
需要满足的条件
- 高可用
- 低消耗
- 可重入
- 非阻塞
- 锁自动续约
- 锁超时失效
实现方式
数据库
唯一索引实现
创建一张带有唯一索引的表,插入数据成功表示获取到锁,释放锁则删除数据。
- 优点:
- 不需要使用事务
- 非阻塞
- 缺点:
- 可重入,锁续约,锁失效均需要自主实现,增加了复杂性。
数据库行锁实现
开启事务,对某条数据加锁,加锁成功表示获取到锁,提交事务则释放锁。
- 优点:
- 具备可重入性,事务内可以重复对某行加锁。
- 锁无需续约,事务期间一直持有锁。
- 锁自动失效,异常情况下,数据库连接断开事务自动回滚,及时释放锁。
- 缺点:
- 部分数据库引擎不支持行锁
- 部分数据库引擎不支持非阻塞
- 使用数据库事务消耗较大
redis
普通实现
使用SETNX命令设置一个键,并设置过期时间,释放锁时需要先判断锁是否失效再删除键(使用lua脚本)。
- 优点:
- 性能高,使用简单。
- 缺点:
- 需要实现锁续约功能。
- 多实例架构下主从复制未完成主节点即宕机会造成锁丢失。
redlock
多实例架构下的解决方案,要求至少五个主节点(互相完全独立且无从节点),在超过半数的实例上获取锁成功且总耗时小于锁过期时间才视为获取锁成功,不推荐使用,因为流程太过繁重,且同样无法解决NPC问题(Network Delay & Process Pause & Clock Drift)。
zookeeper
通过临时顺序节点实现,如果创建的子节点序号最小则表示获取锁成功,释放锁则删除节点。
- 优点:
- 锁无需续约,连接建立期间,节点一直存在。
- 锁自动失效,异常情况下,连接断开,临时节点自动删除,及时释放锁。
- 有锁等待队列,其他线程可以监听节点操作,删除节点后获取通知。
- 缺点:
- 需要频繁创建节点和删除节点,性能较低。
- 如果客户端无法维持心跳,zookeeper会误认为session过期而删除节点导致锁释放。