分布式锁

分布式锁

基于数据库

  • 乐观锁(数据更新之前先查状态,起冲突就更新失败)

  • 悲观锁(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客户端支持多种重试策略。多次重试之后还不行的话才会删除临时节点。

分享: