当前位置:网站首页>读已提交级别下 注解事务+分布式锁结合引起的事故--活动购买机会的错乱
读已提交级别下 注解事务+分布式锁结合引起的事故--活动购买机会的错乱
2022-07-17 12:35:00 【名字是乱打的】
背景: 我们这里有个限购活动可以对某些商品进行机会限购,用户可以通过积极参与平台游戏或者购物等获取购买机会。今天突然收到系统告警,有大量异常错误码。 事故现象: 看了下记录是给17万用户每人加了两次购买机会,而且每个人加机会是不是一次加够,而是业务测采用每调一次接口加一次机会的形式...业务层分了8万组数据,每组一个用户,每组并发调两次机会增加接口,事故造成17万用户里的350余名用户无法正常下单,受损用户比较少,还没上报完问题就有告警中心邮件发来了,在客诉之前解决问题; 事故大概原因: 排查了一下,发现这是一场由
Mysql Read COMMIT级别+注解事务+分布式锁,当系统收到极端高并发情况(μs级)下引起的事故。 三个结合在一起产生的特殊bug。 下面由我细细道来
一. 业务简单伪代码贴一下:
/**
* 机会增加接口
XXXXXXX等符号是我手动打码行为
*/
@Transactional(rollbackFor = Exception.class) //注意,就是这里有问题
@PostMapping("chanceAdd")
public XxxDto chanceAdd(@RequestBody xxxReq req) {
// 快速去重\快速失败机制(借鉴AQS的addWaiter)----除此之外后面还有数据库唯一键做保底持久去重
if (!redisUtils.setExNx(REPEAT_CHECK_PRE +XXX orderNo XXXXX)) {// 业务订单号判重,同一笔交易只能增加一次机会
throw new CommonException(ApplicationCode.REPEAT_SUBMIT,"重复添加机会");
}
//按人+商家+活动分配一把锁
RLock lock = redissonClient.getLock(REPEAT_CHECK_PRE +XXX人,商家id,活动idXXXXX);
lock.lock();
try {
//活动添加记录增加
final boolean saveRes = quotaExtChanceAddRecordService.save(ExtChanceAddRecordMapping.INSTANCE.toQuotaAddRecordPojo(req));
if (saveRes) {
//该人总机会增加
//查询是否已经存在用户总机会记录
UserExtChance userExtChance = service.getUserExtChance(req.getUserId(), req.getMallId(), req.getActivityId());
if (userExtChance==null){//如果用户购买记录不存在
//生成用户对该活动的总机会记录
}else {//已存在
//对已有机会记录做增加
}
}
} catch (Exception e) {
log.error("chanceAdd,data:{},errorMsg:{}",req.toString(),e.getMessage());
throw new CommonException(ApplicationCode.REPEAT_SUBMIT);
} finally {
lock.unlock();
}
return new XxxDto();
}二.错误原因分析
我们按照代码线分析,模拟异常情况
- 事务开启没有问题
- 这里的红锁也可以保障分布式情况下对单人单商家单活动添加机会的串行化
- 但是假如有两个线程A,B并发去调这个接口,可能出现A释放锁未提交事务,B获取锁由于A未提交的事务,获取的是A提交之前的快照,因此做出了错误判断
- 至此 A,B均对于同一用户生成了两条总机会记录。或者出现了数据覆盖的问题(其他可能情况)。
错误流程模拟,分析
三.总结
本次错误原因是虽然我们用红锁保障了特定机会((用户,商家,活动)维度)增加的串行化,但是我们这里事务是用的注解事务导致事务在方法结束之后才提交,因此Read COMMIT级别下,并发情况可能读到了未变更的数据,导致做出错误判断
四.解决
改成声明式事务,在业务结束后提交事务或者异常回滚事务,重点要在串行化结束之前(这里是获取到红锁之前)完成整个事务的操作;
多亏系统各种告警配置....在用户还没发现之前就把问题暴露出来了,一天内完成了问题暴露,找到原因,测试复现,开发解决,发布测试,上线,刷数据,复测验证整个流程;
建议只有极简单的事务用注解事务,复杂业务还是手动比较好。 另外注意只要我方主动加锁的一般都是咱们知道这里肯定有潜在并发问题,在测试人员测试时候必须让测试人员多测几十组,确保咱们的防并发没问题; 我们这个业务之前也让测试人员测试了,用了30组 30qps的并发,但是由于这里确实比较偶发,所以没出现问题...这次是1W多组并发出现了问题;
边栏推荐
- Microsoft OneNote tutorial, how to insert mathematical formulas in OneNote?
- How to save and exit VIM
- NJCTF 2017messager
- 图神经网络的可解释性方法介绍和GNNExplainer解释预测的代码示例
- 机械臂速成小指南(零点五):机械臂相关资源
- 顺序表的基本建立,以及增删改查的相关操作(c语言描述之顺序表)
- JMeter中如何实现接口之间的关联?
- What is pytest? Automated testing is a must
- Ffmpeg record video, stop (vb.net, step on the pit, class library - 10)
- HCIA OSPF
猜你喜欢
随机推荐
Analysis of the "Cyberspace Security" competition of Hunan secondary vocational group in 2022 (super detailed)
Feature level fusion in Bev space
HCIA static comprehensive experiment report 7.10
HCIA 静态基础实验 7.8
私钥,公钥的区分——私钥公钥讲解
顺序表的基本建立,以及增删改查的相关操作(c语言描述之顺序表)
Figure an introduction to the interpretable method of neural network and a code example of gnnexplainer interpreting prediction
什么是pytest,自动化测试必学
2022 Shaanxi secondary vocational group "Cyberspace Security" - packet analysis
R语言使用R原生函数进行数据聚合统计(Aggregating transforms)计算滑动窗口统计值(Window Statistics)、计算滑动分组最小值(min)并合并生成的统计数据到原数据集
C # treeview tree structure recursive processing (enterprise group type hierarchical tree display)
STM32F407 NVIC
AutoJs学习-多功能宝箱-中
通过中序遍历和前序遍历,后续遍历来构建二叉树
How to realize the association between interfaces in JMeter?
HCIA 复习作答 2022.7.6
Huawei wireless device configuration intelligent roaming
2022年全国最新消防设施操作员(中级消防设施操作员)模拟试题及答案
R language uses the aggregate function of epidisplay package to divide numerical variables into different subsets based on factor variables, calculate the summary statistics of each subset, and set na
开发第一个Flink应用




![[PostgreSQL] PostgreSQL 15 optimizes distinct](/img/18/5aaae76c1c269960defc7db8a9e63f.png)




