资讯专栏INFORMATION COLUMN

分布式锁的原理和实现详解

ZoomQuiet / 2615人阅读

摘要:分布式情况下这个就需要换成分布式锁以保证数据一致性。三分布式锁实现原理所需的依赖一个拥有强一致性的服务发现存储仓库。保证数据一致性一个具有高可用性的服务发现存储仓库。

一、基本概念

</>复制代码

  1. 分布式锁,是单机锁的一种扩展,主要是为了锁住分布式系统中不同机器代码的物理块或逻辑块。以此保证不同机器之间的逻辑一致性。
二、一个简单的案例 对DB写操作的双检锁案例

伪代码如下

</>复制代码

  1. if (可以插入一条数据) {
  2. lock {
  3. if (可以插入一条数据) {
  4. // 插入一条数据
  5. }
  6. }
  7. }

上面代码中的lock如果是单机情况下的锁的话,在一台服务器多线程情况下是没有问题的。但是如果是分布式的情况下,单机的锁只能锁住一台服务器的物理代码块,是无法防住其他机器产生的脏数据的。

分布式情况下这个lock就需要换成分布式锁以保证数据一致性。

三、分布式锁实现原理 所需的依赖

一个拥有强一致性的服务发现存储仓库。(保证数据一致性)

一个具有高可用性的服务发现存储仓库。(保证服务的稳定性)

在这里我们使用etcd作为服务发现存储仓库。

具体实现原理 我们本次所使用的etcd的能力

etcd节点的保持独占能力。(同一时间只有一台服务器可以拥有节点)

节点事件监听能力。(可以在节点被释放/占有时通知观察者-服务器)

节点主动释放能力。(服务器可以主动释放拥有节点)

节点超时自动释放能力。(超时自动释放节点)

在代码中,需要准备以下几个内容

etcd长监听。(监听etcd节点,在节点变更时作出响应)

事件队列。(存放争夺etcd节点的方法)

争夺锁事件。

等待锁超时事件。(没画在流程图中,在争夺锁事件入队列时注册,持有锁时注销)

持有锁超时事件。(没画在流程图中,在持有锁时注册,在释放锁时注销)

释放锁事件。

基本代码流程图

正常代码流程图(markdown画的,有点丑)

</>复制代码

  1. st=>start: 项目启动
  2. watcher=>operation: 监听etcd
  3. ed=>end: 等待请求
  4. st->watcher->ed

</>复制代码

  1. st=>start: 开始执行lock
  2. ed=>end: 结束
  3. watcher=>operation: 监听etcd
  4. hasEvent=>condition: 事件队列为空
  5. pushEvent=>operation: 争夺锁事件 入等待队列
  6. watcherEvent=>operation: etcd节点超时事件和释放事件
  7. popEvent=>operation: 争夺锁事件 出事件队列
  8. execute=>operation: 执行被锁住的代码块
  9. fight=>condition: 争夺锁失败
  10. unlock=>operation: 释放锁
  11. st->fight
  12. fight(no)->execute->unlock->ed
  13. fight(yes)->pushEvent->watcherEvent->popEvent->fight

事件细节伪代码

准备一个清空所有相关事件的万能方法

</>复制代码

  1. 假设eventQueue是这种业务对应的事件队列
  2. 假设fightEvent是该次执行的争夺锁事件
  3. 假设clear为清除超时事件的方法
  4. 假设holdEvent为持有锁超时事件
  5. 假设waitEvent为等待锁超时事件
  6. 假设unLockEvent为释放锁事件
  7. // 清空所有事件 - clearAll
  8. eventQueue.remove(fightEvent)
  9. clear(waitEvent)
  10. clear(holdEvent)

监听etcd的两个事件

</>复制代码

  1. // 当etcd锁超时事件或etcd主动释放事件发生时
  2. // 争夺锁事件 出事件队列
  3. fightEvent = eventQueue.pop
  4. // 执行争夺锁事件
  5. 执行 fightEvent

争夺锁事件

</>复制代码

  1. // 争夺锁事件
  2. 执行 etcd争夺锁方法
  3. if (抢到锁了) {
  4. // 清空所有事件
  5. 执行 clearAll
  6. // 设置持有锁超时事件
  7. 设置 holdEvent
  8. } else {
  9. // 争夺锁事件如果是二次入队列,建议到队列头,而不是到队列尾
  10. eventQueue.push(fightEvent)
  11. }

等待锁超时事件(用时间轮延时执行)

</>复制代码

  1. // 清空所有事件
  2. 执行 clearAll
  3. // 抛出异常
  4. throw new Exception

持有锁超时事件(用时间轮延时执行)

</>复制代码

  1. // 执行释放锁事件
  2. 执行 unLockEvent

释放锁事件

</>复制代码

  1. // 释放锁事件
  2. 执行 etcd释放锁方法
  3. // 清空所有事件
  4. 执行 clearAll

备注

</>复制代码

  1. 以上代码可以用有限状态机设计模式来设计业务

图解分布式服务器与etcd集群交互

项目启动时初始化,服务器A、B、C是一个分布式系统(不考虑哪台为master)

同时发来10个请求,此时经过算法分配假设变成了这样

开始争夺锁时,10个线程同时向etcd争夺锁

假设请求2争夺成功,那么第一次竞争完毕后除了请求2,其他请求都进入了各自的事件队列等待etcd释放锁的通知

当请求2执行完代码块后,向etcd发送释放锁请求

etcd收到释放锁请求,将节点删除,触发compareAndDelete事件(释放锁事件)

三台服务器接收到释放锁的信息,各自让事件队列头的请求向etcd发送争夺锁请求

锁抢完后,重复4->6的行为

补充说明

etcd还有一个功能是控制时序,这样的话就可以将每个请求抢锁的行为控制到只剩一次,业务执行的顺序由etcd控制,不过我没有试过,有兴趣的同学可以试一试。

之前基于分布式锁的原理还写了一个分布式缓存锁,是通过锁缓存来防止缓存击穿的,有空的时候补上。感谢各位同学看我的文章看到此处。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/28571.html

相关文章

  • 布式锁的原理实现详解

    摘要:分布式情况下这个就需要换成分布式锁以保证数据一致性。三分布式锁实现原理所需的依赖一个拥有强一致性的服务发现存储仓库。保证数据一致性一个具有高可用性的服务发现存储仓库。 一、基本概念 分布式锁,是单机锁的一种扩展,主要是为了锁住分布式系统中不同机器代码的物理块或逻辑块。以此保证不同机器之间的逻辑一致性。 二、一个简单的案例 对DB写操作的双检锁案例 伪代码如下 if (可以插入一条数...

    young.li 评论0 收藏0
  • 基于 Zookeeper 的布式实现

    摘要:不过比较肤浅,为了进一步加深对的认识,我利用空闲时间编写了本篇文章对应的基于的分布式锁实现。不过我所编写的分布式锁还是比较简陋的,实现的也不够优美,仅仅是个练习,仅供参考使用。好了,题外话就说到这里,接下来我们就来聊聊基于的分布式锁实现。 1. 背景 最近在学习 Zookeeper,在刚开始接触 Zookeeper 的时候,完全不知道 Zookeeper 有什么用。且很多资料都是将 Z...

    邱勇 评论0 收藏0
  • 【推荐】最新200篇:技术文章整理

    摘要:作为面试官,我是如何甄别应聘者的包装程度语言和等其他语言的对比分析和主从复制的原理详解和持久化的原理是什么面试中经常被问到的持久化与恢复实现故障恢复自动化详解哨兵技术查漏补缺最易错过的技术要点大扫盲意外宕机不难解决,但你真的懂数据恢复吗每秒 作为面试官,我是如何甄别应聘者的包装程度Go语言和Java、python等其他语言的对比分析 Redis和MySQL Redis:主从复制的原理详...

    BicycleWarrior 评论0 收藏0
  • 【推荐】最新200篇:技术文章整理

    摘要:作为面试官,我是如何甄别应聘者的包装程度语言和等其他语言的对比分析和主从复制的原理详解和持久化的原理是什么面试中经常被问到的持久化与恢复实现故障恢复自动化详解哨兵技术查漏补缺最易错过的技术要点大扫盲意外宕机不难解决,但你真的懂数据恢复吗每秒 作为面试官,我是如何甄别应聘者的包装程度Go语言和Java、python等其他语言的对比分析 Redis和MySQL Redis:主从复制的原理详...

    tommego 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<