点击上方蓝色“程序猿DD”,选择“设为星标”

回复“资源”获取独家整理的学习资料!

来源 | cnblogs.com/chopper-poet/p/10802242.html

前言

提到数据一致性、操作原子性,诸如此类的一些与并发有关的词汇时不知道你第一时间会联想到什么呢?我相信大多数人可能会想到“锁”,为什么是锁呢,这个我不多说,大家心里应该都明白。在单体应用时代,我们使用jvm提供的锁就可以很好的工作,但是到了分布式应用时代,jvm提供的锁就行不通了,那么势必要借助一些跨jvm的临界资源来支持锁的相关语义,比如redis,zookeeper等。

步入正题

我今天就来分享下我司基于redis来实现的分布式锁,2013年投入使用,也算是久经沙场。但是也存在一些设计上的缺陷,这个我后面也会提到,希望大家秉着互相学习的态度文明交流,别一上来就说这不行那不行,还是那句话“适合自己的才是最好的”。

加锁过程分析

我第一次读代码的时候,有这么几个疑惑:

Q1:为什么不使用 SET key value [expiration EX seconds|PX milliseconds] [NX|XX]  这个指令来实现key的自动过期呢,反而放到应用代码判断key是否过期?

A1:我们的分布式锁开发的时候SET命令还不支持NX、PX,所以才想出这种办法来实现key过期,NX、PX在2.6.12以后开始支持;

Q2:已经判断了当前key对应的时间戳已经过期了,为什么还要使用getset再获取一次呢,直接使用set指令覆盖不可以吗?

A2:这里其实牵扯到并发的一些事情,如果直接使用set,那有可能多个客户端会同时获取到锁,如果使用getset然后判断旧值是否过期就不会有这个问题,设想一下如下场景:

1.C1加锁成功,不巧的是,这时C1意外的奔溃了,自然就不会释放锁;

2.C2,C3尝试加锁,这时key已存在,所以C2,C3去判断key是否已过期,这里假设key已经过期了,所以C2,C3使用set指令去设置值,那两个都会加锁成功,这就闯大祸了;如果使用getset指令,然后判断下返回值是否过期就可以避免这种问题,假如C2跑的快,那C3判断返回的时间戳已经过期,自然就加锁失败;

释放锁过程分析

Q1:为什么释放锁时还需要判断key是否过期呢,直接del不是性能更高吗?

A1:考虑这样一种场景:

**1.**C1获取锁成功,开始执行自己的操作,不幸的是C1这时被阻塞了;

**2.**C2这时来获取锁,由于C1被阻塞了很长时间,所以key对应的value已经过期了,这时C2通过getset加锁成功;

**3.**C1尘封了太久终于被再次唤醒,对于释放锁这件事它可是认真的,伴随着一波del操作,悲剧即将发生;

**4.**C3来获取锁,好家伙,居然一下就成功了,接着就是一波操作猛如虎,接着就是一堆的客诉过来了;

为什么会这样呢?回想C1被唤醒以后的事情,居然敢直接del,C2活都没干完呢,锁就被C1给释放了,这时C3来直接就加锁成功,所以为了安全起见C3释放锁时得分成两步:1.判断value是否已经过期 2.如果已过期直接忽略,如果没过期就执行del。这样就真的安全了吗?安全了吗?安全了吗?假如第一步和第二步之间相隔了很久是不是也会出现锁被其他人释放的问题呢?是吧?是的!有没有别的解决办法呢?听说借助lua就可以解决这个问题了。

正视自己的缺点

Q1:Redis锁的过期时间小于业务的执行时间该如何续期?

A1:这个暂时没有实现,据说有一个叫Redisson的家伙解决了这个问题,我们也有部分业务在使用,未来有可能会切换到Redisson。

Q2:怎么实现的高可用?

A2:我们采用Failover机制,初始化redis锁的时候会维护一个redis连接池,加锁或者释放锁的时候采用多写的方式来保障一致性,如果某个节点不可用的时候会自动切换到其他节点,但是这种机制可能会导致多个客户端同时获取到锁的情况,考虑这种情况:

1. C1去redis1加锁,加锁成功后会写到redis2,redis3;

2 . C2也去redis1加锁,但是此时C2到redis1的网络出现问题,这时C2切换到redis2去加锁,由于第一步中的redis多写并不是原子的,所有就有可能导致C2也获取锁成功;

针对这种情况,目前有些业务方是通过数据库唯一索引的方式来规避的,未来会修复这个bug,具体方案目前还没有。

总结

希望对有些同学能起到帮助,不喜勿喷。

往期推荐

IDEA不愧为神器,结合Groovy脚本,简直无敌!

只需4步,自己搞个 Spring Boot Starter !

离职成为自由开发者的第 100 天

ArrayList 为什么要实现 RandomAccess 接口?

为什么阿里规定需要在事务注解@Transactional中指定rollbackFor?

离职成为自由开发者的100天

我在星球与你分享经验、交流成长

???? ???? ???? ????

星球两大分享内容

公司用了 6 年的分布式锁,很是牛逼啊!相关推荐

  1. 公司来了个新人,他好像有社交牛逼症......

  2. 快来学习Redis 分布式锁的背后原理

    以前在学校做小项目的时候,用到Redis,基本也只是用来当作缓存.可阿粉在工作中发现,Redis在生产中并不只是当作缓存这么简单.在阿粉接触到的项目中,Redis起到了一个分布式锁的作用,具体情况是这 ...

  3. 还不知道 Redis 分布式锁的背后原理?还不赶快学习一下

    前言 以前在学校做小项目的时候,用到Redis,基本也只是用来当作缓存.可阿粉在工作中发现,Redis在生产中并不只是当作缓存这么简单.在阿粉接触到的项目中,Redis起到了一个分布式锁的作用,具体情 ...

  4. Redis分布式锁的实现原理看这篇就够了~

    2019独角兽企业重金招聘Python工程师标准>>> 一.写在前面 现在面试,一般都会聊聊分布式系统这块的东西.通常面试官都会从服务框架(Spring Cloud.Dubbo)聊起 ...

  5. 面试必问:如何实现Redis分布式锁

    摘要:今天我们来聊聊分布式锁这块知识,具体的来看看Redis分布式锁的实现原理. 一.写在前面 现在面试,一般都会聊聊分布式系统这块的东西.通常面试官都会从服务框架(Spring Cloud.Dubb ...

  6. redis 分布式锁 看门狗_redis分布式锁原理及实现

    一.写在前面 现在面试,一般都会聊聊分布式系统这块的东西.通常面试官都会从服务框架(Spring Cloud.Dubbo)聊起,一路聊到分布式事务.分布式锁.ZooKeeper等知识. 所以咱们这篇文 ...

  7. redisson的锁的类型_再有人问你分布式锁是什么,就把这个丢给他!

    [小宅按]现在面试都会聊聊分布式系统,通常面试官都会从服务框架(Spring Cloud.Dubbo),一路聊到分布式事务.分布式锁.ZooKeeper 等知识.今天就来聊聊分布式锁这块的知识,先具体 ...

  8. redis分布式锁原理及实现

    一.写在前面 现在面试,一般都会聊聊分布式系统这块的东西.通常面试官都会从服务框架(Spring Cloud.Dubbo)聊起,一路聊到分布式事务.分布式锁.ZooKeeper等知识. 所以咱们这篇文 ...

  9. 万字长文!不为人所知的分布式锁实现全都在这里了

    1. 引入业务场景 首先来由一个场景引入: 最近老板接了一个大单子,允许在某终端设备安装我们的APP,终端设备厂商日活起码得几十万到百万级别,这个APP也是近期产品根据市场竞品分析设计出来的,几个小码 ...

最新文章

  1. P1642 规划 01分数规划+树形DP
  2. VMware SDS 之四:VSAN的技术细节
  3. NET插件系统之四——提升系统搜索插件和启动速度的思考
  4. latex 数学公式_技能分享——LaTeX篇I
  5. Java虚拟机JVM的内部体系结构
  6. How to Plan My Life?
  7. series、dataframe转为tensor格式数据
  8. margin和padding的四种写法
  9. wc 统计文件字节数、字符数、单词数
  10. 经典古诗文与人工智能创作诗歌 之诗三百(AI)
  11. UltraCompare 乱码的解决
  12. 开关电源电路组成及常见各模块电路分析
  13. 如何解决3G模块和USB转串口冲突问题
  14. 人工智能管家机器人应当具备哪些功能?拥有家电控制能力是优势
  15. 商品id- item_id /条形码/skuid
  16. 趣味博弈论——斐波那契博弈
  17. 实现isPrime()函数,参数为整数,要有异常处理,如果是质数返回True,否则返回False
  18. WAV文件格式解析及处理
  19. 浅谈什么是web应用防火墙(WAF)
  20. 基于改进YOLOv7&OpenCV的行人过马路速度与交通灯实时监测系统(源码&教程)

热门文章

  1. 修改ie9默认的quirk模式
  2. nmap 添加自定义指纹
  3. linux系统 克隆 恢复 Clonezilla 再生龙 使用
  4. linux centos grub grub2 加密、清除
  5. linux 系统调用表 sys_call_table 获取方法
  6. 百度搜索 带网页特效的关键词
  7. Android 中设置只是程序第一次运行才显示的界面
  8. 数据结构 - 有两个链表,第一个升序,第二个降序,合并为一个升序链表(C++)
  9. eclipse编辑java_15个小type:教你高效使用Eclipse Java IDE
  10. Ubuntu中安装docker-compose