分布式下必备神器之分布式锁

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 今天这篇文章我们来聊聊在分布式环境下的一个神兵利器——分布式锁!在看这篇文章的时候,默认大家对锁已经了解了,如果不了解的朋友可以去翻翻公号前面的文章,有很多篇详细介绍了锁的一些知识。写这篇文章的主要原因是之前星球中有朋友说面试中被问的频率有点高,虽然知道分布式锁是什么,但是还是不能很好的说出来,这篇文章就是帮助大家好好梳理一下分布式锁的原理,希望对大家有帮助。另外欢迎到 Java 极客技术知识星球中,我们一起煮酒论技术~

什么是分布式锁


首先我们先来简单了解一下什么是分布式锁(关于什么是锁,可以翻翻之前公号的文章或者到我们的网站 http://www.justdojava.com/ 上看看之前的文章)。


在引入分布式锁之前大家应该都知道经典的 CAP 理论提到任何一个系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)三者的,同一时刻只能满足两个,在这种情况下分布式锁就出现了,分布式锁就是用来解决数据一致性问题的。


在以前单体应用的环境下,Java 的 API 提供了很多控制并发的接口,包括 synchronized 以及 JUC 下面的一些实现,但是在分布式环境下这些 API 就没有用武之地了,因为应用是多实例部署的,很多实例甚至都不在同一台主机上,根本无法使用 Java 中的 API,这个时候分布式锁就诞生了。


所以简单说什么是分布式锁,分布式锁就是在分布式环境下用来解决多实例对数据访问一致性的一种技术方案


使用场景


在实际环境中我们有很多场景会用到分布式锁,例如全局计数器,只要涉及到多个实例进程对同一份数据进行修改等操作都会需要分布式锁。在比如在下单,更新缓存,减少库存等场景下也会用到分布式锁的。

分布式锁的特性


在看分布式锁的实现之前,我们先了解下一个分布式锁应该具备哪些特性:


  1. 在分布式环境下同一时刻只能被单个线程获取;
  2. 可重入,意思是已经获得锁的线程在执行的过程中不需要再次获得锁;
  3. 异常或者超时自动删除,避免死锁;
  4. 高性能,分布式环境下必须要性能好;



实现方式


分布式锁的实现方式流行的主要有三种,分别是基于缓存 Redis 的实现方式,基于 ZK 临时顺序节点的实现以及基于数据库行锁的实现。这里简单提供下实现思路,不重复造轮子因为网上已经有很多开源的很好的解决方案了。


基于 Redis 缓存的实现


首先我们来看下基于 Redis 缓存实现的分布式锁,Redis 支持 SETNX 命令,表示设置一个 key 的值当且进度 Key 不存在的时候才能设置成功。例如执行如下命令:set ziyou 18 NX PX 10000 表示将名叫 ziyou 的 key 的值设置为 18,当且仅当不存在名为 ziyou 的 key 的时候才能设置成功,并且过期时间设置为 10 秒钟。


setnx 命令是 Redis 实现分布式锁的核心,这个命令操作是原子操作的,千万不能分两步先用 set 再用 expire,这样分开操作不是原子性的,无法实现效果。


然后 Redis 分布式锁在网上有开源实现 [Redission](https://github.com/redisson/redisson),具体的实现可以参考。


百度也有一个开源的分布式 Redis 锁叫 [dlock](https://github.com/baidu/dlock),我们采用就是这个,目前使用这么久还没出现什么问题。使用方式类似下面:

69.jpg

优缺点


优点:


1. 实现简单;

2. 理解逻辑简单;

3. 性能好,毕竟是缓存。


缺点:


1. Redis 容易单点故障,集群部署;

2. key 的过期时间设置多少不明确,只能根据实际情况调整。


基于 ZK 的实现


前面提到 Redis 的核心的是 SETNX 命令,那么对于 ZK 来说,实现分布式锁的核心是临时顺序节点。首先关于 ZK 的知识我们后面有机会再跟大家介绍,目前我们只要知道 ZK 的节点种类中有一种叫做临时顺序节点,两个关键词:临时顺序


临时表示在客户端创建某节点后,如果客户端经过一段时间跟服务端之间失去了心跳,说明客户端已经掉线了,那么这个节点就会被自动删除(这一点跟 Redis key 的过期时间类似);顺序的意思是在一个 node 下面生成的子节点是按顺序的,每个子节点都有一个唯一编号,并且这个编号是按顺序自增的。


临时顺序节点再加上 ZK 的监听机制就可以实现分布式锁了,Curator 是一个 ZK 的开源客户端,也提供了分布式锁的实现,这个我没用实际用过,但是网上用的人也很多,大家可以自己去研究一下。


优缺点


优点:


1. ZK 本身就是集群部署,避免单机故障;

2. 顺序节点所以不用考虑过期时间设置问题;


缺点:


1. 实现较为复杂;

2. 非缓存机制,大量频繁创建删除节点会影响 ZK 集群性能;


基于数据库的实现


基于数据库的分布式锁个人觉得性能不是很好,在高并发的情况下对数据库服务器的压力过大,会影响业务,不建议使用。不过从学习的角度来看,我们还是有必要了解下具体的实现方式。基于数据库的分布式锁的实现大致有两种方式,这里的数据库我们以 MySQL 为例。两种方案的实现都需要一个额外的表,并且要有一个唯一索引字段。


1. 阻塞式语句 select xxx for update

2. 非阻塞试  insert into xxx ; delete from


解释下:


第一种方案在实施的时候,需要关闭事务的自动提交,然后执行 SQL 去获得锁,如果获得锁成功,执行下面的业务逻辑,如果这里没有获取到锁,则会阻塞,一直等待。业务执行结束后,手动提交事务。这里如果程序在执行提交事务失败,异常或者服务宕机后,数据库会自动释放锁,以免导致死锁。但是这里有个问题就是如果在高并发的情况下,很多线程都没有获得到锁,都在阻塞等待,这样会导致数据库的服务器压力过大,会影响数据库的服务。这个是要注意的,这也是我不建议的地方,容易出现瓶颈,毕竟没有缓存高效。


第二种方案跟第一种类似,不一样的地方是这里通过第一步向指定的表中插入一条唯一索引的数据,插入成功则表示获得锁,插入失败则未获得到锁,成功获得的锁后就可以执行业务逻辑,在执行完业务逻辑后就可以删除执行的记录。如果插入失败就需要重新触发获取锁的动作。但是这种方案存在的问题是无法设置锁的失效时间,需要其他手段来清理超时数据,而且为了支持可重入,需要将主机和服务的信息一起保存。


优缺点


优点


1. 容易理解和实现,但是细节要注意;


缺点:


1. 高并发的情况下性能不好,阻塞式的情况下很多链接不释放会拖垮数据库服务;

2. 需要定时清理超时数据,麻烦;

3. 数据库的行锁会因为 MySQL 的查询优化而失效



小结


这篇文章主要跟大家介绍了一下分布式锁的使用场景和实现逻辑,知道了具体的逻辑,代码实现可以参考很多开源的实现,理解了原理再造轮子或者修改轮子会深刻很多。在现在的分布式环境下,很好的理解分布式锁是一个很重要的点,希望能帮助到大家,最后欢迎到我们 Java 极客技术的知识星球中探讨技术,如果想了解其他方面的技术也可以跟我们提,我们互相学习共同进步。知识星球期待你的加入。


相关实践学习
基于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
相关文章
|
1月前
|
NoSQL 算法 安全
Redlock 算法-主从redis分布式锁主节点宕机锁丢失的问题
Redlock 算法-主从redis分布式锁主节点宕机锁丢失的问题
152 0
|
4月前
|
缓存 NoSQL 算法
认真学习分布式应用中的分布式锁
认真学习分布式应用中的分布式锁
43 0
|
3月前
|
缓存 算法 NoSQL
【分布式详解】一致性算法、全局唯一ID、分布式锁、分布式事务、 分布式缓存、分布式任务、分布式会话
分布式系统通过副本控制协议,使得从系统外部读取系统内部各个副本的数据在一定的约束条件下相同,称之为副本一致性(consistency)。副本一致性是针对分布式系统而言的,不是针对某一个副本而言。强一致性(strong consistency):任何时刻任何用户或节点都可以读到最近一次成功更新的副本数据。强一致性是程度最高的一致性要求,也是实践中最难以实现的一致性。单调一致性(monotonic consistency):任何时刻,任何用户一旦读到某个数据在某次更新后的值,这个用户不会再读到比这个值更旧的值。
386 0
|
24天前
|
NoSQL Java Redis
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件(二)
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的分布式锁的功能组件
15 0
|
3月前
|
NoSQL 中间件 API
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)(下)
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)
81 2
|
存储 关系型数据库 MySQL
分布式事物【悲观锁、乐观锁、读锁、写锁、间隙锁、临键锁 、 表锁、行锁、页面锁、 如何避免死锁】(二)-全面详解(学习总结---从入门到深化)
分布式事物【悲观锁、乐观锁、读锁、写锁、间隙锁、临键锁 、 表锁、行锁、页面锁、 如何避免死锁】(二)-全面详解(学习总结---从入门到深化)
48 0
|
14天前
|
缓存 NoSQL 数据库
关于高并发下缓存失效的问题(本地锁 && 分布式锁 && Redission 详解)
关于高并发下缓存失效的问题(本地锁 && 分布式锁 && Redission 详解)
29 0
|
1月前
|
NoSQL Java Redis
Redis分布式锁和Java锁的区别
Redis分布式锁和Java锁的主要区别在于它们的适用范围和实现机制。
41 2
|
2月前
|
NoSQL Java API
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)
分布式锁【数据库乐观锁实现的分布式锁、Zookeeper分布式锁原理、Redis实现的分布式锁】(三)-全面详解(学习总结---从入门到深化)
298 0
|
3月前
|
NoSQL Java 数据库
分布式事务的锁
分布式事务的锁
26 0
分布式事务的锁

热门文章

最新文章