知识点-分布式锁

213 阅读2分钟

1.背景

在多线程编程中,常会使用到mutex、atomic等锁,用以处理共享资源的同步。在分布式场景下,多实例之间同样存在共享资源同步的问题,今天我们一起聊聊常见分布式锁的实现

2.简介

分布式锁本质上是提供了,一个全局的唯一的管理锁的服务,提供获取、释放锁的能力

2.1 作用

根据Martin的观点,分布式锁按照用途图区分,主要是解决了效率提升和正确性保证的事情

0.效率:针对幂等服务,避免重复操作,以使效率提升,即使锁失效也不影响正确性
1.正确性:一旦无法互斥访问,即会发生数据的不一致

  • 去重(非幂等服务例如重复下单,MQ去重(生产者或者消费者导致的重复))
  • 串行化(例如:订单协同)
2.2 特性
  • 互斥性 - 同一时间只有一个服务能获得锁
  • 安全性 - 避免误解锁
  • 可重入 - 避免死锁
  • 容错性 - 服务异常仍能正常释放锁
2.3 目标
  • 高可用
  • 一致性

3.分布式相关概念

  • CAP定理
    任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。在分布式场景,首先要保证分区容错性,所以系统的设计主要是在 AP 和 CP 之间进行选择

  • "惊群效应"
    惊群现象就是多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),当等待的这个事件发生时,他会唤醒等待的所有进程(或者线程),最终却只有一个进程(线程)获得这个事件的"控制权",其他进程(线程)只能重新进入休眠状态,这种现象和性能(上下文切换、进程调度等)浪费就叫做惊群效应

  • 公平锁 & 非公平锁
    公平锁:即先到先获得锁,后到依次排队获得
    非公平锁:每次发起都先尝试获得锁,失败则排队等待获取

4.实现

4.1 步骤
  • 加锁
  • 解锁
4.2 常见实现

1.redis

  • 单线程处理,set命令
  • 加锁:set key value [EX seconds] [PX milliseconds] [NX|XX],2.6.12前的版本,key值和超时设置为两个指令,可通过lua脚本,保持原子性
  • 解锁:利用lua脚本,判断是否为当前线程持有,再删除锁

2.1 zookeeper
临时节点、节点顺序

2.2 etcd
Revision 自增ID

demo代码:github.com/apollo-go/d…

4.3 异常情况
  • 持有者崩溃
    <= 超时解锁

  • 误解除   <= 持锁方ID

  • 超时解锁
    => 线程守护

  • STW(针对A获取锁后异常挂起,服务端释放锁,B又获取到锁后的异常情况)
    自增ID,资源访问前增加ID大小验证
    非正常释放锁时,延长锁服务的释放时间

文档:
www.infoq.cn/article/how…
juejin.cn/post/684490…