dubbo重试(retry)导致事务重复执行提交

本文详细介绍了在公司模块更新后,由于数据库出现重复数据引发的问题。作者通过排查发现,问题源于Dubbo服务调用超时触发的重试机制,导致Service被重复执行,从而引发了事务的双重提交。分析还指出,当超时重试时,由于前一个事务未完成,第二个事务尝试访问相同数据,造成锁表并可能导致超时错误。最后,作者强调了避免业务逻辑超过Dubbo超时时间的重要性,以防止类似问题的发生。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.背景

公司有个模块最近有新需求,因此有些地方需要改动,而测试同学在测的过程中发现有个地方报了错让我看看,于是我看了一下报错日志,是因为数据库有重复数据,初步推断应该是在写接口导致的数据写入多条。

2.排查过程

​ 我先是把重复数据手动删除,然后尝试复现同事的操作。发现在编辑的时候,接口返回时长特别慢,导致了报错。然后数据库多了一条重复数据,最开始的想法是,是不是在界面的操作中出现了重复点击这个问题,于是我本地连接测试数据库进行调试。

​ 我在controller层和service层都打上了断点,然后重新调用了几次,发现有几次是产生了重复数据的,但是有几次又没有。同事之前写这段代码的时候,不是利用@Transactional注解而是利用的手动在代码中处理事务(代码如下),我在提交事务和回滚事务的地方都打上了断点,发现有时候tx.commit(status)会被断点拦截两次,而有时候tx.commit(status);tx.rollback(status);各会被断点拦截一次,但是controller层的断点却又只被拦截一次,于是我判断这不是来自重复点击的问题,因为如果重复点击,controller层也会被调用两次才对。

事务控制

​ 那会是什么问题呢?我看了一下controller层报错,报错信息如下,这段信息表名,controller层在利用dubbo调用service时,出现了超时的情况,我们超时时长设置的是60s,并且设置了reties=1,即超时重试机制,于是我明白了,应该是controller在调用service出现超时后,重试机制再调用了service一次,导致了service被执行了两次,导致事务重复提交,所以数据库出现了重复数据。

duboo调用超时报错

​ 可是为什么有时候不会出现重复数据呢?我继续打断点寻找,终于,在一次没有出现重复数据时发现了以下报错。就是说在dubbo重试机制执行时,重新开启事务,但是因为前面的事务没有结束,而后面的事务操作的数据跟前一个事务操作的是一样的数据,所以导致第一个事务锁住了表,第二个事务就需要等待第一个事务执行完,但是有时候第一个事务执行的时间过长,导致第二个事务锁超时。这样也就出现了有时会出现重复数据,有时不会。

事务执行超时报错

3. 总结

在使用dubbo的时候,业务逻辑不要超过dubbo设置的超时时长,不然会触发dubbo的重试机制,导致service被重复调用执行

<think>嗯,用户这次问的是Seata全局事务超时机制的具体行为,特别是超过60秒是否会触发回滚。从对话历史看,用户之前已经了解过超时的影响,现在更关注执行过程的触发条件。 用户可能正在调试一个超时场景,需要确认Seata的自动回滚机制。ta引用的资料很专业,说明对AT模式有基础认知,但引用[5]提到手动回滚后仍触发提交的异常,这可能是ta实际遇到的坑。 需要重点解释三点:1) TC的定时检测原理 2) 超时判定边界 3) 与手动回滚的冲突避免。尤其要强调配置项transaction.timeout和client.rm.lock.retryTimeout的区别,前者控制全局事务生命周期,后者影响锁竞争。 用户没提具体业务场景,但引用[3]涉及服务调用链超时,推测可能是跨服务事务。要提醒http超时(如Feign)可能早于全局超时触发,这点在分布式环境下特别关键。 技术细节上,必须澄清一个误区:超时回滚不是精确的60秒整,而是TC轮询周期+网络延迟的综合结果。引用[4]的会话存储机制会影响状态更新时效性,redis比db模式响应更快。 最后补充的优化建议应该聚焦可操作项,比如如何通过seata-server调整timeout阈值,或者用APM工具监控事务生命周期。用户明显需要落地解决方案而非纯理论。</think>根据Seata的设计机制,**当全局事务执行时间超过配置的超时时间(如默认60秒)时,Seata会自动触发全局回滚**。以下是具体原理和流程: --- ### 一、超时触发全局回滚的机制 1. **TC(事务协调器)的定时检测** Seata的TC会通过定时任务扫描所有进行中的全局事务状态。若发现某个全局事务的**存活时间超过配置的超时阈值**(例如60秒),TC会主动发起回滚指令[^1][^3]。 2. **分支事务的状态同步** 当分支事务(微服务)执行超时或抛出异常时,Seata拦截器会捕获异常并向TC报告分支事务失败。若此时全局事务已超时,TC将直接触发全局回滚[^1][^5]。 3. **关键配置项** 超时阈值由`transaction.timeout`参数控制(默认60秒)。若需调整,可在`seata-server`配置文件中修改: ```properties transaction.timeout=120000 # 单位毫秒(120秒) ``` --- ### 二、超时回滚的完整流程 ```mermaid graph TD A[全局事务开始] --> B[TC记录事务状态] B --> C{是否超时?} C -- 超时 --> D[TC发起全局回滚] C -- 未超时 --> E[继续执行分支事务] D --> F[通知所有分支事务回滚] F --> G[分支事务执行undo_log回滚] G --> H[释放全局锁] ``` 1. **TC检测超时** TC每隔固定间隔(默认1秒)扫描事务状态,超时事务会被标记为`TimeoutRollbacking`[^3][^4]。 2. **异步回滚分支事务** TC向所有参与的分支事务(RM)发送回滚指令,各RM根据`undo_log`反向生成补偿SQL,回滚数据变更[^2][^3]。 3. **清理资源** 回滚完成后,TC释放全局锁并删除事务会话记录,避免资源泄露[^4][^5]。 --- ### 三、超时回滚的注意事项 1. **与手动回滚的冲突** 若代码中已手动回滚(如`GlobalTransactionContext.reportRollback()`),但全局事务状态仍为`Active`,后续TC触发的自动回滚可能导致错误日志(如`Global transaction not found`)[^5]。 **解决方案**:确保手动回滚后更新事务状态为`Rollbacked`。 2. **超时阈值设置建议** - 高并发场景:建议≤60秒,减少锁竞争风险 - 批处理任务:可适当延长,但需满足: ```math \text{超时时间} > \sum(\text{所有分支事务最大执行时间}) ``` 3. **避坑指南** - 确保服务间调用超时(如Feign/Dubbo)**小于**Seata全局超时时间,避免网络超时导致误回滚[^1][^3]。 - 监控`tm_commit_retry_count`(提交重试次数)和`lock_retry_timeout`(锁重试超时)参数,优化锁竞争[^2]。 --- ### 四、总结 | **场景** | **结果** | **触发方** | |-------------------------|------------------------|------------| | 全局事务超时(>60s) | 自动触发全局回滚 | TC | | 分支事务抛出未捕获异常 | 触发全局回滚 | RM→TC | | 手动回滚后事务状态未更新| 可能报错`Global transaction not found` | TC | **结论**: > Seata严格遵循超时机制,事务超过配置时间未完成时,**必定触发全局回滚**。需通过合理配置超时阈值、协调服务间调用超时、及时更新事务状态来避免意外错误[^1][^3][^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值