Transacation 引发血崩问题分析与思考

事件回顾

在业务高峰期并发量较大时引起大量事务不提交现象,并且业务线程基本被block、不能对外提供服务

查询事务mysql事务: SELECT * from information_schema.INNODB_TRX it ;

dump java应用栈信息:jstack $pid

线上出问题期间没有dump到栈信息,直接回滚了代码版本,后续花费了大量时间进行压测验证,最后定位到类似以下代码出现问题


@Transaction(transactionManager = "datasource1")
public void testTrans() {
    // test1Mapper 绑定datasource1
    test1Mapper.insert(***);
    test1Mapper.update(****);

    // test2Mapper 绑定datasource2
    test2Mapper.insert(***);
}

在上线之前review到这段代码第一反应  test2Mapper.insert(***); 出现错误不会回滚 test1Mapper中的sql操作,由于上线时间临近,改动代码+提测 可能来不及,即使不回滚对业务的影响不大,可后期修复数据

万万没想到的是,正式因为这个事务方法在业务高峰期引起了雪崩。。。。。。  怪自己太肤浅

直接原因

1. 在业务高峰期,mysql连接池比较繁忙

2. 进入事务方法执行test1Mapper中的两条sql使用连接1,因开启事务,连接1绑定到了事务上下文,在事务未结束之前不释放

3. test2Mapper在执行sql时,因绑定不同数据源则会重新获取连接2,若此时恰好连接池没有空闲连接则进入等待,当前线程被block,等待时间由checkoutTimeout(c3p0数据源)控制,线上设置30秒

4. 持有连接1,但获取不到连接2,恰好又是业务高峰期,进一步加剧了连接池的连接竞争

5. 很快其他非事务方法操作sql时也获取不到连接造成大量业务线程等待,引起雪崩

为什么上面代码会引起高峰期雪崩,需要从spring 事务机制、数据源、连接池来分析下原因

原因分析

数据源 与 连接池

连接池:应用与db连接缓存起来,进行复用,减小创建链接带来的开销

数据源:封装连接池中创建、销毁、获取、退还 连接, 等操作 提供统一接口


Druid数据源类:

public class DruidDataSource extends... {
    // 连接数据    
    private volatile DruidConnectionHolder[] connections; 
}

C3P0数据源相关类:

class BasicResourcePool implements ResourcePool
{
  
    int target_pool_size;

    // 存储连接
    /*  keys are all valid, managed resources, value is a PunchCard */
    HashMap  managed = new HashMap();

    /* all valid, managed resources currently available for checkout */
    LinkedList unused = new LinkedList();

    /* resources which have been invalidated somehow, but which are */
    /* still checked out and in use.                                */
    HashSet  excluded = new HashSet();
}

业务方法执行事务流程

1. 开启事务

2. 执行事务方法

3. 提交 或 回滚

4. 恢复事务设置 

第4步是执行 set autocommit = 1,  没有事务时默认autocommit=1,第1步开启事务时 会执行set autocommit = 0;且会调用mysql执行该语句

spring @Transaction 分析

进入业务方法之前开启事务,创建事务对象,通过@Transaction注解中的数据源获取连接,并绑定到threadlocal中

每次从数据源中获取连接时若无空隙连接则当前线程被阻塞

,绑定连接后,在进入事务体方法执行sql时则会从threadlocal中获取已绑定连接执行sql

简化代码

public class DataSourceTransactionManager ....{
    @Override
	protected void doBegin(Object transaction, TransactionDefinition definition) {
		DataSourceTran
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值