点击上方“何俊林”,马上关注,每天早上8:50准时推送

真爱,请置顶或星标

转自:踩刀诗人

www.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,具体方案目前还没有。

说句题外话,有不少人想加鱼哥微信,鱼哥姑且放出来,但是坑位有限哦

想去大厂么?来加入鱼哥技术星球,放大你进大厂概率

扫描如下二维码加入我的知识星球:

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

  1. 公司用了 6 年的分布式锁,很是牛逼啊!

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | cnblogs.com/chopper-poe ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

最新文章

  1. 从小乡村走出的985高校博导坦言:读书这条路最简单!
  2. vijos 1030 重叠的方框
  3. 也谈表达式分析和计算
  4. 【Tiny4412】烧写最小文件系统到SD卡
  5. 如何处理db2中文不显示
  6. php ajax队列,AJAX请求队列实现
  7. Django之templates模板
  8. c语言拔河分组回溯算法,【阅读下面的文字,完成10—12题。文明的共相回溯我们历史演-查字典问答网...
  9. transferwise怎么提现_收款工具transfer wise介绍(多币种、可收CJ联盟)
  10. React+Webpack+Webstorm开发环境搭建
  11. SQLSERVER函数判断当天是星期几
  12. 管理感悟:没有活跃用户量,谈广告都是开玩笑
  13. 移远百科 | GNSS定位技术知多少
  14. 斜率优化理解【16年 沈阳区域赛 The Elder 】
  15. 骗子、假先知们一夜暴富背后:区块链是回归互联网本来意义的唯一希望 | 深度
  16. 20210505 秀米导入已发布微信推送的所有内容
  17. 【问链财经-区块链基础知识系列】 第四十五课 一文读懂保理业务的操作流程
  18. 计算机系大二学期计划范文,大二学期学习计划范文6篇
  19. 漫步者和南卡蓝牙耳机哪个好?高性价比蓝牙耳机测评
  20. 题目-Android基础

热门文章

  1. SQL:postgresqll查询某字段各情况数量
  2. Javascript:遍历json数据的方法
  3. string类用法Java_Java中String类的用法
  4. datatable的查询介绍
  5. 论文笔记_S2D.05-2012-ECCV-从立体图像中提取与场景一致的三维对象和深度
  6. 目录_计算机视觉——算法及应用
  7. java resultSet获取总行数
  8. leetcode之移除链表的元素
  9. MyEclipse中VSS的使用详解
  10. vc10的C2664和C2065错误