一文搞懂分布式锁的原理与实现

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云原生内存数据库 Tair,内存型 2GB
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 对于锁,大家应该都不陌生,手机上可以加锁,想用时候解锁,不用的时候上锁,那自行车、房门同样可以加把锁,道理属于类似的情况。

前言


对于锁,大家应该都不陌生,手机上可以加锁,想用时候解锁,不用的时候上锁,那自行车、房门同样可以加把锁,道理属于类似的情况。


微信图片_20220608213221.jpg


在日常开发工作中,我们为了保证资源操作的最终一致性,同样需要用到锁来进行操作控制。本Chat结合自己工作中的经验沉淀,来跟大家一起聊聊 分布式锁的那些事,分享一些实用内容给大家。


为什么会出现分布式锁?


如下图所示,一个应用被部署到多个机器上做负载均衡。为了保证一个方法或属性在高并发情况下的同一时间只能被同一个线程执行,我们该如何解决这个问题呢?


微信图片_20220608213224.png


在传统单体应用单机部署的情况下,可以使用并发处理相关的功能(如Java并发处理相关的API:ReentrantLcok或synchronized)进行互斥控制来解决。但是,随着业务的发展,系统架构也会逐步优化升级,原本单体单机部署的系统被演化成分布式集群系统,由于分布式系统多线程、多进程并且分布在多个不同机器上,这将使原单机部署情况下的并发控制锁策略无法满足,并不能提供分布式锁的能力。为了解决这个问题就需要一种跨机器的互斥机制来控制共享资源的访问,这就是分布式锁解决的难题!


分布式锁应用场景有哪些?


针对分布式锁的目的来反向推导其应用场景,主要包括两类:


1、处理效率提升:应用分布式锁,可以减少重复任务的执行,避免资源处理效率的浪费;


2、数据准确性保障:使用分布式锁可以放在数据资源的并发访问,避免数据不一致情况,甚至数据损失等。


分布式锁的实现前提


分布式的CAP理论:


任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。


通常情况下,大家都会牺牲强一致性来换取系统的高可用性,这样我们很多的场景,其实是只需为了保证数据的“最终一致性”。


需要注意的是,这个最终时间需要是用户可以接受的范围内的。


另外,要实现分布式锁,需要具备一些条件,主要包括以下几项:


1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;


2、获取锁与释放锁的高可用及高性能;


3、具备非阻塞锁特性,获取不到锁将直接返回获取锁失败;


4、具备锁失效机制,防止死锁。


上述条件,主要突出锁本身的提效和保障准确性的应用特性,同时避免其本身对资源访问造成影响;


实现方式有哪些呢?


关于分布式锁的实现,可以分别控制在不同的环节。


微信图片_20220608213227.jpg


常见的主要分为以下这几种:


1、开源组件锁控制:ZooKeeper


ZooKeeper 是一个分布式协调服务的开源框架。主要用来解决分布式集群中应用系统的一致性的问题,例如怎样避免同时操作同一数据造成脏读的问题。ZooKeeper 本质上是一个分布式的小文件存储系统。提供基于类似于文件系统的目录树方式的数据存储,并且可以对树种的节点进行有效管理。


微信图片_20220608213230.png


那如何使用ZooKeeper实现分布式锁?


1)客户端连接zookeeper,并在/tmp下创建临时的且有序的子节点,第一个客户端对应的子节点为lock-0000,第二个为lock-0001,以此类推;


2)客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听刚好在自己之前一位的子节点删除消息,获得子节点变更通知后重复此步骤直至获得锁;


例如/tmp下的子节点列表为:lock-0000、lock-0001、lock-0002,序号为1的客户端监听序号为0的子节点删除消息,序号为2的监听序号为1的子节点删除消息。(业务代码执行完即删除子节点)


3)执行业务代码流程,删除当前客户端对应的子节点,锁释放。


ZooKeeper分布式锁方式,性能相对Redis方式较差,主要原因是写操作(获取锁释放锁)都需要在Leader上执行,然后同步到follower。


2、任务处理锁控制:Redis


Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。


主要的优势包括:


  • 性能极高 – Redis能读的速度是11w+次/s,写的速度是8w+次/s


  • 丰富的数据类型 – Redis主要支持 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型


  • 原子性 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行


  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。


Redis实现简单分布式锁过程:


(1)获取锁:使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。


(2)获取锁:设置一个获取的超时时间,若超过这个时间则放弃获取锁。


(3)释放锁:通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。


微信图片_20220608213232.png


利用Redis实现分布式锁,实现可能存在的缺点:


在执行delete进行释放锁的时候,假如操作删除锁动作失败,那此 Key-Value 过期时间则不好控制,可能会一直存在,可能对后续数据验证造成影响。


数据写入锁控制:MySQL


数据库层面是最终数据写入的时候,对数据做写入控制处理,算是分布式锁的最终末端环节。主要包括以下三种方式,下面分别介绍一下。


实现方式一:唯一索引


UNIQUE KEY `uidx_name` (`name`) USING BTREE;


上述case中,我们对 name 字段做了索引的唯一性约束,当存在多个新增数据请求同时提交到数据库的话,数据库自身则会利用唯一索引,来保证数据的唯一性。


实现方式二:排他锁


执行以下SQL:


SELECT status FROM users WHERE id = 3 FOR UPDATE;


假如,在另一个事务中再次执行:


SELECT status FROM users WHERE id = 3 FOR UPDATE;


则第二个事务会一直等待上一个事务的提交,此时第二个查询处于阻塞的状态。


排它锁的应用:


在进行事务操作时,通过 “FOR UPDATE” 语句,MySQL会对查询结果集中每行数据都添加排他锁,其他线程对该记录的更新与删除操作都会阻塞。排他锁包含行锁、表锁。


实现方式三:乐观锁


实现逻辑:乐观锁每次在执行数据修改操作时,都会带上一个数据版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1 操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现 ABA 问题。


除了 version 以外,还可以使用时间戳,因为时间戳天然具有顺序递增性。


比较麻烦的一点:就是在操作业务前,需要先查询出当前的 version 版本。

数据库分布式锁实现可能存在的缺点:


  • DB操作性能较差,并且有锁表的风险;


  • 非阻塞操作失败后,需要轮询,占用cpu资源;


  • 长时间不commit或者长时间轮询,可能会占用较多连接资源


总结


上面的几种分布式锁的实现,需要根据不同的应用场景选择最适合的实现方式。

在分布式环境中,对资源进行上锁有时候是很重要的,比如秒杀、抢购某一资源,这时候使用分布式锁就可以很好地控制资源。同时,在具体应用过程中,还需要考虑很多的因素,比如超时时间的选取,获取锁时间的选取对并发量等等,上述各方式实现的分布式锁仅作为一种简单的实现的参考,主要了解其中的思想。

面对任何问题,希望大家可以多做些深入分析,了解本质问题之后再考虑解决办法进行解决,希望大家能够掌握问题分析以及解决的能力,去触类旁通,做到游刃有余。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
3月前
|
设计模式 安全 Java
【分布式技术专题】「Tomcat技术专题」 探索Tomcat技术架构设计模式的奥秘(Server和Service组件原理分析)
【分布式技术专题】「Tomcat技术专题」 探索Tomcat技术架构设计模式的奥秘(Server和Service组件原理分析)
76 0
|
3月前
|
存储 分布式计算 Hadoop
Hadoop【基础知识 01】【分布式文件系统HDFS设计原理+特点+存储原理】(部分图片来源于网络)
【4月更文挑战第3天】Hadoop【基础知识 01】【分布式文件系统HDFS设计原理+特点+存储原理】(部分图片来源于网络)
178 3
|
3月前
|
存储 分布式计算 监控
Hadoop【基础知识 01+02】【分布式文件系统HDFS设计原理+特点+存储原理】(部分图片来源于网络)【分布式计算框架MapReduce核心概念+编程模型+combiner&partitioner+词频统计案例解析与进阶+作业的生命周期】(图片来源于网络)
【4月更文挑战第3天】【分布式文件系统HDFS设计原理+特点+存储原理】(部分图片来源于网络)【分布式计算框架MapReduce核心概念+编程模型+combiner&partitioner+词频统计案例解析与进阶+作业的生命周期】(图片来源于网络)
236 2
|
1月前
|
NoSQL Redis 数据库
|
3月前
|
存储 NoSQL 分布式数据库
【Flink】Flink分布式快照的原理是什么?
【4月更文挑战第21天】【Flink】Flink分布式快照的原理是什么?
|
3月前
|
缓存 算法 关系型数据库
深度思考:雪花算法snowflake分布式id生成原理详解
雪花算法snowflake是一种优秀的分布式ID生成方案,其优点突出:它能生成全局唯一且递增的ID,确保了数据的一致性和准确性;同时,该算法灵活性强,可自定义各部分bit位,满足不同业务场景的需求;此外,雪花算法生成ID的速度快,效率高,能有效应对高并发场景,是分布式系统中不可或缺的组件。
878 2
深度思考:雪花算法snowflake分布式id生成原理详解
|
3月前
|
算法 安全
金石原创 |【分布式技术专题】「分布式技术架构」一文带你厘清分布式事务协议及分布式一致性协议的算法原理和核心流程机制(Paxos篇)
金石原创 |【分布式技术专题】「分布式技术架构」一文带你厘清分布式事务协议及分布式一致性协议的算法原理和核心流程机制(Paxos篇)
275 1
金石原创 |【分布式技术专题】「分布式技术架构」一文带你厘清分布式事务协议及分布式一致性协议的算法原理和核心流程机制(Paxos篇)
|
3月前
|
存储 运维 分布式计算
面经:HDFS分布式文件系统原理与故障排查
【4月更文挑战第10天】本文深入剖析了HDFS的底层原理和面试重点,包括HDFS的架构(NameNode、DataNode、Secondary NameNode)、文件读写流程、高级特性(快照、Erasure Coding、Federation、High Availability)以及故障排查方法。通过HDFS Shell命令示例,加强理解,并对比了HDFS与其他分布式文件系统的优缺点。掌握这些知识将有助于求职者在面试中脱颖而出,应对HDFS相关技术考察。
133 3
|
3月前
|
消息中间件 存储 监控
解析RocketMQ:高性能分布式消息队列的原理与应用
RocketMQ是阿里开源的高性能分布式消息队列,具备低延迟、高吞吐和高可靠性,广泛应用于电商、金融等领域。其核心概念包括Topic、Producer、Consumer、Message和Name Server/Broker。RocketMQ支持异步通信、系统解耦、异步处理和流量削峰。关键特性有分布式架构、顺序消息、高可用性设计和消息事务。提供发布/订阅和点对点模型,以及消息过滤功能。通过集群模式、存储方式、发送和消费方式的选择进行性能优化。RocketMQ易于部署,可与Spring集成,并与Kafka等系统对比各有优势,拥有丰富的生态系统。
561 4
|
3月前
|
存储 供应链 安全
区块链技术原理及应用:深入探索分布式账本技术
【4月更文挑战第30天】区块链,从加密货币的底层技术延伸至多元领域,以其分布式账本、去中心化、不可篡改性及加密技术重塑数据存储与交易。核心组件包括区块、链和节点,应用涵盖加密货币、供应链管理、金融服务等。尽管面临扩展性等挑战,未来潜力无限。

热门文章

最新文章