当前位置:首页 > 公众号精选 > 架构师社区
[导读]最近小莱去大厂面试,最终挂在了分布式锁上,于是回来后认真整理了这篇文章,以期下次面试遇到同样的问题时一雪前耻......


最近小莱去大厂面试,最终挂在了分布式锁上,于是回来后认真整理了这篇文章,以期下次面试遇到同样的问题时一雪前耻......

什么是分布式锁

分布式锁是控制分布式系统之间同步访问共享资源的一种方式。举个通俗易懂的例子:网吧打游戏。


小莱去网吧打游戏,路上碰巧遇到了同学小王和小丁,三人同时来到网吧前台表示都想在包厢里上网。但是包厢只有一个,同一时间也只能容纳一人,前台MM很为难。突然,前台MM心生一计,将一枚硬币抛于空中,让他们三人同时争抢,谁能抢到谁去包厢。只见小莱眼疾手快最终将硬币据为己有,看着不甘的小王和小丁,哼着小曲进了包厢.....


在这个例子中,小莱、小王和小丁可以看成三个独立分布的客户端(三个独立系统),小莱在包厢上网的时间看作锁的时间,包厢可以看作同一资源。同一时刻三人都想去包厢(即都想访问同一资源),那么硬币就可以作为一把分布式锁限制同一时刻共享包厢的人员。

分布式锁的场景

当多个机器(多个进程)对同一数据进行修改时,并且要求这个修改是原子性的,那么就要用到分布式锁。例如:秒杀时解决库存超卖问题。

分布式锁的特点

1、互斥性

任意时刻,只有一个客户端能够持有锁。


2、不会发送死锁

即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。


3、容错性

只要大部分的redis节点正常运行,客户端就可以加锁和解锁。


4、解锁

加锁和解锁必须为同一个客户端,客户端不能解锁他人的锁。

分布式锁的实现

  • 基于redis实现

  • 基于mysql乐观锁实现

  • 基于zookeeper实现


在这篇文章中,我们重点来讲述如何通过redis来实现分布式锁。

加锁实现方式

常用redis命令

  • setnx:在指定的key不存在时,为key设置指定值

  • expire:设置key过期时间,单位以秒计

  • getset:设置指定key的值,并返回key的旧值


错误示例

1、通过setnx、expire实现



实现思路:在当前锁没有被占用的情况下,加锁成功后,给锁设置一个过期时间。


这乍看没有什么问题,但是仔细思考之后就会发现由于setnx/expire不具有原子性,某一时刻进程在执行expire前突然崩溃,就会导致该锁永久存在,后续进程在获取锁时发现锁已被占用,从而导致无法加锁。


2、将锁的值设为过期时间,通过锁的值对比实现



这段代码实现的缺点是:

  1. 需要分布式下每个客户端的时间保持一致;

  2. 锁快过期时,多个客户端同时执行getSet,虽然最终只有一个客户端可以加锁,但该客户端锁的过期时间可能被其他客户端覆盖;

  3. 不具备拥有者标识,任何客户端都可以解锁。


正确示例



参数说明:

  • nx:SET IF NOT EXIST,即当key不存在时,进行set操作,若key已经存在,则不做任何操作

  • px:给key加一个过期时间,单位ms


redis2.8版本后,set里提供了px参数,因此我们在实现分布式锁的时候就可以进行原子操作,同时加锁操作也变得简单。


通过上述示例,我们已经清楚地知道了加锁的实现方式,但是解铃还须系铃人,解锁如何实现呢?

解锁实现方式

常用redis命令

  • del:用于删除已存在的键

  • pttl:以毫秒为单位返回key的剩余过期时间


错误示例

1、最常见的一种错误解锁方式是直接通过删除del来进行的:


这种方式的错误在于不具有拥有者标识,任何客户端都可以随时进行解锁。


2、有人可能会说,加锁时给每个客户端分配一个唯一的value值,每次释放锁前把锁的值与客户端传过来的值做对比,相同再删除不就行了,即:



这种方式确实在一般情况下能够解决锁被其他客户端随意释放的问题,但是这样实现会有什么问题呢?答案是当客户端A在执行del之前,锁突然过期了,此时客户端B加锁成功,然后客户端A执行del操作则会将客户端B的锁解除。这还是因为删除不具有原子性。


:在这里还有一种解决临界条件下客户端A锁被其他客户端释放的方式,只是对性能可能有一些影响:在del前,我们可以先判断锁的过期时间,如果当前时间不小于10ms(根据自己的业务而定)的话可以操作del删除,否则自然释放,即:



正确示例



锁的释放包含了get、判断、del三个步骤。如果不能保证三个步骤的原子性,分布式锁就会有并发问题。


通过redis里eval命令操作lua代码,这样可以确保在解锁时保持原子性,而不会因为进程的崩溃导致解锁失败。

思考

到这里我们就完成了分布式锁的实现,请继续思考:


一、当在集群中,某个master节点宕机后,master数据未及时同步至slave节点时,上述示例是否还能满足当前场景?此时会发生什么样的情况?又该如何来解决?


上边讲述的示例适用于单实例或对业务要求性不高的情况,当在集群上实现分布式锁的时候,master节点宕机且数据未同步至slave节点时,此时就会出现多个客户端拥有一把锁的情况,失去了锁的互斥性原则。


基于此,redis官方提出了RedLock的实现方案,核心思想是同时使用多个Redis Master来冗余,且这些节点是完全独立的,也不需要对这些节点之间的数据进行同步。获取集群中多数master节点上的锁,同时全部获取,否则全部释放。


例如下图的集群中,同时在一半以上(2个master)的master上加锁,如果其中某一个master宕机,客户端仍然可以获取到锁。



Redis cluster集群图


二、业务未处理完面临锁时间到期如何处理?


还是开头那个例子,小莱在包厢里打游戏,任务做到一半,时间到了,这时怎么办呢?有经验的同学第一反应肯定是去续费。


那么对应到锁的应用上也是这样,当占有锁的时间快到了但是此时业务未处理完,可以延长锁的过期时间,即锁支持可重入。

总结

1、无论加锁还是解锁,都要确保原子性操作;

2、Redis分布式锁要考虑单实例和多实例的情况;

3、正确加锁方式:

如果当前业务可容忍多个客户端拥有一把锁或保证master不会发生故障,在集群中也可以使用这种方式。

4、正确解锁方式:

特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:

长按订阅更多精彩▼

如有收获,点个在看,诚挚感谢

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

加利福尼亚州圣克拉拉市—2024年4月30日―AMD(NASDAQ: AMD)今日公布2024年第一季度营业额达55亿美元,毛利率为47%,经营收入3600万美元,净收入1.23亿美元,摊薄后每股收益为0.07美元。基于...

关键字: 嵌入式 PC 人工智能

这款全新的中端MCU系列为设计人员提供了更高水平的安全性和灵活性

关键字: 嵌入式 单片机

2024年4月11日,中国——意法半导体的ST25R100近距离通信(NFC)读取器芯片独步业界,集先进的技术功能、稳定可靠的通信连接和低廉的成本价格于一身,在大规模制造的消费电子和工控设备内,可以提高非接触式互动功能的...

关键字: 嵌入式 数据读取器 芯片

单片机是一种嵌入式系统,它是一块集成电路芯片,内部包含了处理器、存储器和输入输出接口等功能。

关键字: 单片机 编写程序 嵌入式

深圳2024年4月23日 /美通社/ -- 全球AI解决方案与工业级存储领导品牌宜鼎国际 (Innodisk)持续深化边缘AI布局,今(23)日发表全球首创"MIPI over Type-C"独家技术,让旗下嵌入式相机模...

关键字: AI 嵌入式 相机

为增进大家对嵌入式主板的认识,本文将对嵌入式主板以及嵌入式主板常见问题及其解决方法予以介绍。

关键字: 嵌入式 指数 主板

为增进大家对嵌入式系统的认识,本文将对嵌入式系统、嵌入式系统的特点予以介绍。

关键字: 嵌入式 指数 嵌入式系统

为增进大家对嵌入式的认识,本文将对嵌入式、嵌入式工作相关的内容予以介绍。

关键字: 嵌入式 指数 嵌入式技术

机器人操作系统(ROS)驱动程序基于ADI产品而开发,因此可直接在ROS生态系统中使用这些产品。本文将概述如何在应用、产品和系统(例如,自主导航、安全气泡地图和数据收集机器人)中使用和集成这些驱动程序;以及这样将如何有助...

关键字: 电机控制器 机器人 嵌入式

支持高达48V@5A的PD受电模式,达到目前USB PD最高标准。

关键字: 嵌入式 开发板
关闭
关闭