跳至主要內容

分布式锁

HeChuangJun约 657 字大约 2 分钟

如何设计分布式锁?分布式锁怎么实现?√

原子性、可重入性;
锁失效机制,防止死锁;
非阻塞锁特性,即没获取到锁返回获取锁失败,而不是一直等待
高性能、高可用的获取与释放锁

  • 1.正确的获得锁(保证有且只有一个进程获得到) set 指令附带 nx 参数
  • 2.正确的释放锁:使用 Lua 脚本,比对锁持有的是不是自己。如果是,则进行del指令删除来释放。
  • 3.超时的自动释放锁set 指令附带 expire参数,通过过期机制来实现超时释放。
  • 4.未获得到锁的等待机制:sleep或者基于Redis订阅 Pub/Sub 机制。一些业务场景,可能需要支持获得不到锁,直接返回false ,不等待
  • 5.重入性(可选):通过ThreadLocal<Integer>记录是第几次获得相同的锁。有且第一次计数为1&&获得锁时,才向 Redis 发起获得锁的操作;有且计数为 0 && 释放锁时,才向 Redis 发起释放锁的操作。
  • 6、锁超时的处理:可以考虑告警 + 后台线程自动续锁的超时时间。通过这样的机制,保证有且仅有一个线程,正在持有锁。
  • 7、Redis 分布式锁丢失问题 看方案2 Redlock

基于数据库
排他锁:select * from xxx for update
创建一张锁表,数据库对字段作唯一性约束。加锁时增加一条记录;释放锁时删除记录。如果有并发请求同时提交到数据库,数据库会保证只有一个请求能够得到锁。属于数据库IO操作,效率低,而且频繁操作会增大数据库的开销,在高并发的场景中不适用
基于redis:利用命令set key value NX EX 30000或者Redisson
基于zookeeper:利用临时顺序节点;每个线程都创建临时顺序节点,判断获取当前目录下最小的节点(序号)是不是当前节点,如果是则获取锁成功,如果不是则获取锁失败。获取锁失败的线程获取当前节点上一个临时顺序节点并监听,当该节点删除时(上一个线程执行结束删除或者掉线zk删除临时节点)获取到通知代表获取到了锁。公平锁使用临时节点实现,非公平锁使用临时顺序节点实现。或者zookeeper的Curator