事务失效导致线上事故

本文探讨了一次数据库操作中遇到的数据不一致问题,源于事务嵌套和部分提交。通过分析事务时效和传递规则,作者提出了修正措施,如移除私有静态方法的使用并引入AOP代理。为确保业务流程完整性,文章介绍了如何使用Redisson分布式锁进行同步,以避免并发冲突。

先说结果

数据不一致

对外接口中事务部分成功,部分失败,导致数据异常

一个插入方法需要给不同业务插入数据,调用不同方法,部分表数据成功,部分表数据失败

代码调用链中事务有嵌套设置

外层异常,内层正常插入,导致数据异常,操作的是同以数据库,不同的代码包,用POM依赖调用
    原因分析:
        1.事务时效
            1.private、static、final的使用
            解决方法:不在类和方法上使用此类关键字

            2.通过this.xxx(调用当前类的方法)
            使用xml配置方式暴露代理对象.然后在service中通过代理对象AopContext.currentProxy()去调用方法。
        2.事务传递生效规则
            1.内部使用父层事务,如果父层级没有事务,开启新事务

            2.方法内部多层级调用都需要支持事务回滚,我们的数据库层级使用的

MyBatis3Utils.insert执行后直接提交了导致了事务失效

结果确认:

        MyBatis3Utils.insert执行后直接提交了导致了事务失效

        在同一个方法中确定抛异常

异常抛出后前面插入的方法已经在数据库提交,并且没有回滚

解决方案

1.添加MyBatis3Utils.insert事务生效的代码

在service执行方法中添加手动事务

@Autowired
private DataSourceTransactionManager transactionManager;
DefaultTransactionDefinition def = new DefaultTransactionDefinition();//事务定义类
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(def);//返回事务对象。
transactionManager.commit(status);

 

 

2.使用分布式锁实现业务流程完成在执行下一条

决定用分布式锁redisson

import org.redisson.Redisson;
import org.redisson.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.redisson.api.RedissonClient;

/**
 * @Date 2022/2/8 20:29
 * @Version 1.0
 */

@Configuration
public class RedissionConfig {
    Logger log = LoggerFactory.getLogger(RedissionConfig.class);
    
    @Value("${spring.redis.host}")
    String redisIp;
    
    @Value("${spring.redis.port}")
    String redisPort;
    
    @Value("${spring.redis.password}")
    String redisPassword;
    
    @Bean(name="redissonClient")
    public RedissonClient init(){
        Config config = new Config();
        String url = "redis://"+redisIp+":"+redisPort;
        log.info(url);
        config.useSingleServer().setAddress(url).setPassword(redisPassword);
        config.useSingleServer().setConnectionMinimumIdleSize(10);
        RedissonClient redissonClient = Redisson.create(config);
        log.info("初始化RedissonClient成功");
        return redissonClient;
    }
}

业务代码中直接加锁解决

String dataKey = winnerCandidateVO.getDataKey();
RLock lock = redissonClient.getLock("stock:" + dataKey);
lock.lock(10, TimeUnit.SECONDS);  

lock.unlock();

通过加锁确保业务的完整唯一性,记得要释放锁

redisson自带有自旋锁和释放自己加的锁,不用担心多线程释放其他线程的锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

布吉岛的石头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值