首页 > 其他分享 >记一次分布式锁失效的生产事故

记一次分布式锁失效的生产事故

时间:2022-11-17 17:00:47浏览次数:52  
标签:事故 setIfAbsent 任务 实例 失效 定时 执行 分布式

项目背景

  在给某项目做业务开发的时候,有一个任务派发的定时任务,该定时任务通过算法,把系统远远不断的任务每隔一分钟派发给不同的审核员进行审核。因为考虑到分布式任务调度器(如:xxljob/elasticjob)需要单独服务器搭建服务,所以为了减少服务器成本,就自己基于现有资源编写分布式锁,因为现有redis环境,于是就基于redis编写分布式锁,来保证多个实例只有一台实例在同一个时间点在执行派发任务。

 

资源说明

  redis主从模式,1主3从,主从只做备份,不做读写分离。服务:三个实例,定时任务采用自己编写的分布式锁,要求某一时刻只能有一台实例执行定时任务。

 

事故还原:  

  原分布式锁的代码贴图如下:

  

  由图中代码可以看出,出问题的代码那么就是Redis设置锁的.setIfAbsent()这个过程中,导致if中的boolean值判断出错,且这个现象只会偶然发生的,一天一个实例执行 24小时 * 60(每分钟每个实例启动一次定时任务)= 1440次,可能会发生一次或几次分布式锁出错,也可能不发生。生产(三个实例)上线小半个月,发生了一次(然后第二天又发生了两次),查看日志文件,实例A在14:48:00秒开启了定时任务,整个定时任务结束实在14:

boolean success = Boolean.TRUE.equals(redisTemplate.opsForValue()
                    .setIfAbsent(redisKey, value, LOCK_TIME, TimeUnit.SECONDS));

   在网上查看了很多说法,大致意思是:基于Redision的API来实现分布式锁,在使用.setIfAbsent()这个方法的时候可能存在返回 null 的情况。但我寻思着,即使返回为 null ,那么在Boolean.TRUE.equals(null)的结果也应该是false,这样同样会进入if条件,表明该实例设置锁没有成功,不会继续向下执行任务。

  即使我知道不是.setIfAbsent()这个方法返回 null 的情况引起的bug,我还是按照网上对null进行了判断,但是过了两天还是出现了问题,同样是redis分布式锁没有锁住(且只有某一次没有锁住,因为每一分钟在三台实例都会执行一次,我查看了其他时间然后横向横向对比,不常发生但是可能存在发生的风险)

  其中ip为72的实例setIfAbsent()成功,在2022-11-14 15:43:00:007 执行了数据总量查询

  其中ip为73的实例setIfAbsent()成功,在2022-11-14 15:43:00:017 执行了数据总量查询

   两次执行前后相差大概10ms,但是两个都获取到了分布式锁,都往下执行了数据总量查询和日志打印:

// 查询数据总数,用于分批处理
Integer totalCount = subProgramExamineRecordService.countDistributeData(taskDistributeQueryDTO);
log.info("[人审任务分配]-查询待分配的数据重量:{}条,当前时间:{}", totalCount, LocalDateTime.now());

  下图源码截图分析:

 

  其他时段redis分布锁没有问题的部分截图

 

  如果单就if()中的条件判断没有问题,那么唯一出问题的地方就是redisTemplate.opsForValue().setIfAbsent(),即底层存在.setIfAbsent()前后两次设置同一个key都可能返回成功的情况

 

验证猜想:

  于是我带着上面的疑问编写一下面一段代码,使用三个线程模拟三个实例去竞争分布式锁,只有抢到分布式锁的实例才能执行业务逻辑,结果就验证了我上面的猜想,在进行分布式锁的时候,在极少数的概率下,多个实例可能同时竞争到一个分部署锁。

  先看代码:

   while循环大概跑了10分钟,大概跑了几千次三个实例并发挣钱分布式锁,出现了一次两个实例同时获取到了分布式锁,结束了while循环

  运行结果截图:

  自此验证结果完毕,问题也找到了,就是在并发情况下,存在极小的概率执行redisTemplate.opsForValue().setIfAbsent()都能设置分布式锁成功(即多个实例可以获取到同一个分布式锁)。

  

解决办法:

  代码贴图如下:

 

标签:事故,setIfAbsent,任务,实例,失效,定时,执行,分布式
From: https://www.cnblogs.com/yanzige/p/16877578.html

相关文章

  • mysql left join 左关联on条件失效问题
     sqljoin失效问题:1.leftjoin:以左表为基准,根据on条件过滤连接生成临时表,on后面的过滤条件对左表无效2.rightjoin:以右表为基准,根据on条件过滤连接生成临时表,on后......
  • 一次分布式锁与数据库事务的纠缠
    有一个需要保证并发安全性的方法,考虑到锁的粒度与分布式要求,选择了基于Redis的分布式锁。需要保证并发安全性的原因是该方法会并发操作数据库某表中的数据。......
  • 1、Spring事务失效的几种原因
    Spring事务失效的几种原因1、Bean没有被Spring管理,即类上没有加入@Service、@Component等注解。2、方法没有使用public修饰加@Transactional注解的方法必须用public关键......
  • 分布式系统架构master-worker
    概述Master-Workers架构(粗译为主从架构)是分布式系统中常见的一种组织方式。面对分布式系统中一堆分离的机器资源,主从架构是一种最自然、直白的组织方式——就像一群人,有......
  • 记录控件放入QTabWidget 样式失效问题
    控件在QTabWidget外样式正常显示,放到QTabWidget内部后样式失效:例如:暂停/继续按钮放在QTabWidget样式正常 吧这个按钮放到QTabWidget中后: 暂停/继续样式失效......
  • 第一章 分布式系统概述
    一、分布式系统的组成   二、分布式协调组件单机:进程间通信的可行措施有共享内存,信号量,事件单机系统有单点故障缺陷分布式协调组件的作用:对外提供分布式同步服务......
  • MySQL 索引最左前缀原则失效?
    测试索引最左前缀原则,发现缺失带头索引后,索引还是生效的。一、测试创建测试表CREATETABLE`user`(`id`bigint(20)NOTNULLAUTO_INCREMENTCOMMENT'主键',......
  • 12.Seata:Spring Cloud Alibaba分布式事务组件(非常详细)
    随着业务的不断发展,单体架构已经无法满足我们的需求,分布式微服务架构逐渐成为大型互联网平台的首选,但所有使用分布式微服务架构的应用都必须面临一个十分棘手的问题,那就是......
  • 浅谈交流汇流箱在分布式光伏行业中应用​
    应用场景:交流汇流箱作用:交流汇流箱是适用于光伏组串式发电系统中,此汇流箱的进出线采用断路器,采用二级防雷保护,系统额定电压最高为AC690V,防护等级为IP65,达到室外安装的要求,满......
  • Vue.js devtools插件点击Root失效或不显示数据问题的解决方法
    原文链接:https://blog.csdn.net/m0_59183852/article/details/126981557刚开始用这个插件,我就遇到了一个问题:使用该插件对代码进行调试的时候,发现如何点击<Root>都没有反......