锁超时问题

在redis分布式锁中,如果线程A加锁成功了,但是由于业务功能耗时时间很长,超过了设置的超时时间,这时候redis会自动释放线程A加的锁。通常我们加锁的目的是:为了防止访问临界资源时,出现数据异常的情况。比如:线程A在修改数据C的值,线程B也在修改数据C的值,如果不做控制,在并发情况下,数据C的值会出问题。为了保证某个方法,或者段代码的互斥性,即如果线程A执行了某段代码,是不允许其他线程在某一时刻同时执行的,我们可以用synchronized关键字加锁。但这种锁有很大的局限性,只能保证单个节点的互斥性。如果需要在多个节点中保持互斥性,就需要用redis分布式锁。做了这么多铺垫,现在回到正题。

假设线程A加redis分布式锁的代码,包含代码1和代码2两段代码。

由于该线程要执行的业务操作非常耗时,程序在执行完代码1的时,已经到了设置的超时时间,redis自动释放了锁。而代码2还没来得及执行。

此时,代码2相当于裸奔的状态,无法保证互斥性。假如它里面访问了临界资源,并且其他线程也访问了该资源,可能就会出现数据异常的情况。那么,如何解决这个问题呢?如果达到了超时时间,但业务代码还没执行完,需要给锁自动续期。我们可以使用TimerTask类,来实现自动续期的功能:

Timer timer = new Timer();
timer.schedule(new TimerTask() {@Overridepublic void run(Timeout timeout) throws Exception {//自动续期逻辑}
}, 10000, TimeUnit.MILLISECONDS);

获取锁之后,自动开启一个定时任务,每隔10秒钟,自动刷新一次过期时间。这种机制在redisson框架中,有个比较霸气的名字:watch dog,即传说中的看门狗

当然自动续期功能,我们还是优先推荐使用lua脚本实现,比如:

if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then redis.call('pexpire', KEYS[1], ARGV[1]);return 1;
end;
return 0;

需要注意的地方是:在实现自动续期功能时,还需要设置一个总的过期时间,可以跟redisson保持一致,设置成30秒。如果业务代码到了这个总的过期时间,还没有执行完,就不再自动续期了。

主从复制的问题

redis实现分布式锁,对单个redis实例是没有问题的。如果redis存在多个实例。比如:做了主从,或者使用了哨兵模式,基于redis的分布式锁的功能,就会出现问题。具体是什么问题?假设redis现在用的主从模式,1个master节点,3个slave节点。master节点负责写数据,slave节点负责读数据。

本来是和谐共处,相安无事的。redis加锁操作,都在master上进行,加锁成功后,再异步同步给所有的slave。突然有一天,master节点由于某些不可逆的原因,挂掉了。这样需要找一个slave升级为新的master节点,假如slave1被选举出来了。

如果有个锁A比较悲催,刚加锁成功master就挂了,还没来得及同步到slave1。这样会导致新master节点中的锁A丢失了。后面,如果有新的线程,使用锁A加锁,依然可以成功,分布式锁失效了。那么,如何解决这个问题呢?redisson框架为了解决这个问题,提供了一个专门的类:RedissonRedLock,使用了Redlock算法。RedissonRedLock解决问题的思路如下:

  1. 需要搭建几套相互独立的redis环境,假如我们在这里搭建了5套。
  2. 每套环境都有一个redisson node节点。
  3. 多个redisson node节点组成了RedissonRedLock。
  4. 环境包含:单机、主从、哨兵和集群模式,可以是一种或者多种混合。

在这里我们以主从为例,架构图如下:

RedissonRedLock加锁过程如下:

  1. 获取所有的redisson node节点信息,循环向所有的redisson node节点加锁,假设节点数为N,例子中N等于5。
  2. 如果在N个节点当中,有N/2 + 1个节点加锁成功了,那么整个RedissonRedLock加锁是成功的。
  3. 如果在N个节点当中,小于N/2 + 1个节点加锁成功,那么整个RedissonRedLock加锁是失败的。
  4. 如果中途发现各个节点加锁的总耗时,大于等于设置的最大等待时间,则直接返回失败。

从上面可以看出,使用Redlock算法,确实能解决多实例场景中,假如master节点挂了,导致分布式锁失效的问题。但也引出了一些新问题,比如:

  1. 需要额外搭建多套环境,申请更多的资源,需要评估一下成本和性价比。
  2. 如果有N个redisson node节点,需要加锁N次,最少也需要加锁N/2+1次,才知道redlock加锁是否成功。显然,增加了额外的时间成本,有点得不偿失。

由此可见,在实际业务场景,尤其是高并发业务中,RedissonRedLock其实使用的并不多。在分布式环境中,CAP是绕不过去的。CAP指的是在一个分布式系统中:

  • 一致性(Consistency)
  • 可用性(Availability)
  • 分区容错性(Partition tolerance)

这三个要素最多只能同时实现两点,不可能三者兼顾。如果你的实际业务场景,更需要的是保证数据一致性。那么请使用CP类型的分布式锁,比如:zookeeper,它是基于磁盘的,性能可能没那么好,但数据一般不会丢。如果你的实际业务场景,更需要的是保证数据高可用性。那么请使用AP类型的分布式锁,比如:redis,它是基于内存的,性能比较好,但有丢失数据的风险。

Redis分布式锁和@transactional一起使用失效

大家看下这块伪代码:

@Transactional
public void updateDB(int lockKey) {boolean lockFlag = redisLock.lock(lockKey);if (!lockFlag) {throw new RuntimeException(“请稍后再试”);}doBusiness //业务逻辑处理redisLock.unlock(lockKey);
}

在事务中,使用了Redis分布式锁.这个方法一旦执行,事务生效,接着就Redis分布式锁生效,代码执行完后,先释放Redis分布式锁,然后再提交事务数据,最后事务结束。在这个过程中,事务没有提交之前,分布式锁已经被释放,这样就间接导致分布式锁失效了。

springAop,会在updateDB方法之前开启事务,之后再加锁,当锁住的代码执行完成后,再提交事务,因此锁住的代码块执行是在事务之内执行的,可以推断在代码块执行完时,事务还未提交,锁已经被释放,此时其他线程拿到锁之后进行锁住的代码块,读取的库存数据不是最新的。正确的实现方法,可以在updateDB方法之前就上锁,即还没有开事务之前就加锁,那么就可以保证线程的安全性.

Redis分布式锁那点事相关推荐

  1. Redis分布式锁需要考虑的这些事!

    基于 Redis 的分布式锁对大家来说并不陌生,可是你的分布式锁有失败的时候吗?在失败的时候可曾怀疑过你在用的分布式锁真的靠谱吗?以下是结合自己的踩坑经验总结的一些经验之谈. 对不起,网上的那些 Re ...

  2. Redis分布式锁 Spring Schedule实现任务调度

    一看到标题就知道,这一篇博客又是总结分布式工作环境中集群产生的问题,个人觉得分布式没有那么难以理解,可能也是自己见识比较浅,对我来说,分布式只是一种后端业务演进时的一种工作方式,而真正实现这种工作方式 ...

  3. Java分布式锁那点事

    分布式锁那点事 为什么要使用分布式锁 为了保证一个方法在高并发情况下的同一时间只能被同一个线程执行,在传统单体应用单机部署的情况下,可以使用Java并发处理相关的API(如ReentrantLcok或 ...

  4. redis分布式锁的安全性及与zookeeper的对比

    上篇 网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现算法也 ...

  5. 微服务架构之:Redis的分布式锁---搭建生产可用的Redis分布式锁

    Redis分布式锁 集群架构下的并发问题 分布式锁的实现原理和不同方式的实现对比 基于Redis实现的分布式锁 Redis分布式锁1.0版 基于Redis分布式锁1.0版的误删问题 解决误删问题,Re ...

  6. Redis分布式锁解析源码分析

    Redis分布式锁解析&源码分析 概述 实战 简单的分布式锁 Redisson实现分布式锁 Redission源码分析 构造方法 获取锁lock 解锁 锁失效 红锁 案例分析 原始的写法 进化 ...

  7. 面试官:Redis分布式锁解决方案是什么?

    今天博主在这片文章中主要给大家讲下Redis分布式锁的原理以及解决方案 学到三连呦 1.Redis分布式锁原理 1.1.简述 我们知道分布式锁的特性是排他.避免死锁.高可用.分布式锁的实现可以通过数据 ...

  8. redis分布式锁 在集群模式下如何实现_收藏慢慢看系列:简洁实用的Redis分布式锁用法...

    在微服务中很多情况下需要使用到分布式锁功能,而目前比较常见的方案是通过Redis来实现分布式锁,网上关于分布式锁的实现方式有很多,早期主要是基于Redisson等客户端,但在Spring Boot2. ...

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

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

最新文章

  1. 第七周项目一-成员函数(4)
  2. avcodec_encode_video2 AVERROR(EAGAIN)
  3. 关于架构的优化和设计,架构师必须悟透的事情
  4. java中写入文件的方法
  5. Chmod 提升一个文件夹下所有文件(夹)的权限
  6. R语言之连接mysql数据库
  7. Excel通过身份证获取出生年月,性别,年龄,生肖,星座,省份等信息总结归纳...
  8. yeta机器人_Yeta智能语音电话机器人开放平台接入指南(2)
  9. k8s haproxy Unable to connect to the server: x509: certificate is valid for 127.0.0.1, 10.0.1.12
  10. [转] SNDA 并购 MochiMedia
  11. 【css】鼠标禁用样式
  12. 未知usb设备(设备描述请求失败)_USB 之传输事务
  13. 医学自然语言处理(NLP)相关论文汇总之 COLING 2022
  14. htmlunit教程
  15. 旋转关节(Revolute Joint)
  16. 可以下載HEVC(H265)的網址
  17. MSP430时钟设置及应用总结
  18. Win10 + Linux 双系统安装 (Ubuntu 20.04.1 LTS)
  19. 为什么大家说mysql数据库单表最大两千万?依据是啥?
  20. ipmi屏幕(SOL)使用及问题总结

热门文章

  1. js中的indexOf方法和lastIndexOf方法
  2. 《恰到好处的恋爱》经典语录
  3. 微型计算机硬件系统四大部件,微型计算机硬件系统中最核心的部件是( )
  4. ASP.NET用户个性化设置Profile——配置2
  5. 奶块1月25日服务器维护时间,1月25日奶块维护到几点quest; | 手游网游页游攻略大全...
  6. 计算机开机响3声,电脑开机三声短响是什么原因?如何解决?
  7. 苹果手机耗电快_iPhone 手机为什么耗电快?原因在这里
  8. excel批量插入图片(宏方式)
  9. 如何利用今日头条极速版挣点小钱
  10. 2021年山东省安全员C证找解析及山东省安全员C证复审考试