分布式锁
目录:
分布式锁
基于数据库
-
乐观锁(数据更新之前先查状态,起冲突就更新失败)
-
悲观锁(for update,会占有,不利于并发)
- 获取锁的前提:结果集中的数据没有使用排他锁或共享锁时,才能获取锁,否则将会阻塞。
- 需要注意的是,
FOR UPDATE
生效需要同时满足两个条件时才生效:- 数据库的引擎为 innoDB
- 操作位于事务块中(BEGIN/COMMIT)
-
实现:
- 表加唯一索引
- 加锁:insert语句,报错证明加锁失败
- 解锁:delete
-
实现简单
-
数据库开销大、性能不高
-
没有自动超时机制,有死锁风险
基于Redis缓存
- 加锁:SET k v EX 300 NX
- 解锁:DELETE
- 可能会删除不是这个线程拥有的锁
- 解决:验证value(写value的时候也得保证唯一)
- 如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况:
- 在Redis的master节点上拿到了锁;
- 但是这个加锁的key还没有同步到slave节点;
- master故障,发生故障转移,slave节点升级为master节点;
- 导致锁丢失。
基于Zookeper
- 获得锁和等待
- 创建一个持久结点
- C1获得锁:从持久结点创建临时结点Lock1。C1查找所有临时结点并排序,是最前的 一个就获得锁
- C2获取锁,创建临时结点Lock2。查找所有临时结点并排序,发现不是最前,创建watch观察最近的Lock1
- C3获取锁,创建临时结点观察最近的Lock2
- 形成等待队列
- 删除锁
- C1调用删除Lock1 的指令(临时结点在C1断开连接时也会自动删除Lock1)
- C2因为watch着Lock1,删除后立即得到通知
- 再次查询所有临时结点,并排序,发现自己是最小的,获得锁
- acquire方法用户获取锁,release方法用于释放锁。
- 性能不高,因为要创建临时结点
- 对于客户端Client断开的情况,Curator客户端支持多种重试策略。多次重试之后还不行的话才会删除临时节点。