Mysql事务回滚的问题探究

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: Mysql事务回滚的问题探究

前提

XXX平台导出的mysql建表语句没有指定存储引擎,而mysql默认使用MyISAM。

但是MyISAM是不支持事务的。在mysql中,唯有InnoDb是支持事务的。

目的

验证Spring提供的标签@Transactional,以及XXX提供的TransactionComponent组件在不同的存储引擎下的工作实况。

建表语句

借用XXX-BATCH工程中的“batch_cli_user”表部分字段进行实验。

CREATE TABLE `BATCH_CLI_USER` (
  `protocol_no`  VARCHAR(64) NOT NULL COMMENT '协议号',
  `user_id`  VARCHAR(32) COMMENT '用户标识,录入人ID',
  `user_name`  VARCHAR(80) COMMENT '用户名称',
  `contact`  VARCHAR(32) COMMENT '联系人',
  `telephone`  VARCHAR(16) COMMENT '电话号码',
  `mobile_no`  VARCHAR(16) COMMENT '电话号码',
  CONSTRAINT `pk_BATCH_CLI_USER` PRIMARY KEY (`protocol_no`)
) ENGINE = MYISAM;

以及使用Innodb存储引擎的建表语句

CREATE TABLE `BATCH_CLI_USER` (
  `protocol_no`  VARCHAR(64) NOT NULL COMMENT '协议号',
  `user_id`  VARCHAR(32) COMMENT '用户标识,录入人ID',
  `user_name`  VARCHAR(80) COMMENT '用户名称',
  `contact`  VARCHAR(32) COMMENT '联系人',
  `telephone`  VARCHAR(16) COMMENT '电话号码',
  `mobile_no`  VARCHAR(16) COMMENT '电话号码',
  CONSTRAINT `pk_BATCH_CLI_USER` PRIMARY KEY (`protocol_no`)
) ENGINE = INNODB;

实验思路

通过两次对相同主键的插入,若事务生效,则在第二次插入报主键冲突的时候,应当发生回滚,使第一条的插入语句也被回滚,若事务不生效,则查询后可以观察到该记录。

实验过程

测试代码

如果发送事务回滚,则预期为null正确。

@Test
public void testRollbackForMysql() throws Exception {    batchCliUserMapper.deleteByPrimaryKey("testProtocolNo004");    
 BatchCliUser batchCliUser = new BatchCliUser();    
 batchCliUser.setUserId("testUser004");    
 batchCliUser.setProtocolNo("testProtocolNo004");    batchCustomerInfoService.testRollbackForMysql(batchCliUser);    
 BatchCliUser result1 = batchCliUserMapper.selectByPrimaryKey("testProtocolNo004");    Assert.assertTrue(null == result1);
}

1. MyISAM与@Transactional

代码

@Override@Transactional(rollbackFor = Exception.class)
public void testRollbackForMysql(BatchCliUser batchCliUser) throws Exception {    
    // 开启事务,连续插入两次,看事务是否会回滚掉第一次的插入    
    LOGGER.info("开始第一次插入");    
    batchCliUserMapper.insertSelective(batchCliUser);
    LOGGER.info("开始第二次插入");    
    batchCliUserMapper.insertSelective(batchCliUser);}

结果

代码在第二次插入时抛出org.springframework.dao.DuplicateKeyException。但是在数据库中仍然可以查询到该记录,回滚并未生效。

2. MyISAM与TransactionComponent组件

代码

@Override
public void testRollbackForMysql(BatchCliUser batchCliUser) throws Exception {    
    // 显式开启事务,连续插入两次,看事务是否会回滚掉第一次的插入    
    try {        
        transactionComponent.begin();        
        LOGGER.info("开始第一次插入");        
        batchCliUserMapper.insertSelective(batchCliUser);        
        LOGGER.info("开始第二次插入");        
        batchCliUserMapper.insertSelective(batchCliUser);        
        transactionComponent.commit();    
    } catch (Exception e) {        
        transactionComponent.rollback();        
        throw e;    
    }
}

结果

与实验1相同,回滚并未生效。

3. INNODB与@Transactional

代码

与实验1相同

结果

成功回滚,数据库中查无记录。

4. INNODB与TransactionComponent组件

代码

与实验2相同

结果

与实验3相同,成功回滚,数据库中查无记录。

后续探究

TransactionTemplate

Spring提供的事务管理模板,验证可以事务回滚。【且相对XXX提供的组件,更方便设置隔离级别,事务传播特性等】

@Autowired
    public TransactionTemplate transactionTemplate;  
  @Override
  public void testRollbackForMysql(BatchCliUser batchCliUser) throws Exception {        
        // 显式开启事务,连续插入两次,看事务是否会回滚掉第一次的插入        
        transactionTemplate.execute(new TransactionCallbackWithoutResult(){           
            @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                LOGGER.info("开始第一次插入");
                batchCliUserMapper.insertSelective(batchCliUser);
                LOGGER.info("开始第二次插入");
                batchCliUserMapper.insertSelective(batchCliUser);
            }        
        });
    }

xml中的Bean配置

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">   
    <property name="transactionManager" ref="testTransactionManager"></property>
</bean>

深入探究

  1. 三种方式均可完成回滚,那么其到底有什么共同点,在DataSourceTransactionManager类中有如下方法,在debug时发现:
protected void doRollback(DefaultTransactionStatus status) {    DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    if (status.isDebug()) {
           this.logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
       } try {
           con.rollback();
       } catch (SQLException var5) {
           throw new TransactionSystemException("Could not roll back JDBC transaction", var5);
       }
   }

其核心代码在con.rollback()这一行中,交由各个Connection接口的实现类自己实现。

PS: @Transactional注解还需要配置代理,配置如下后:

<!-- 事务管理器 -->
<bean id="testTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">   
    <property name="dataSource" ref="gapsDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="testTransactionManager" proxy-target-class="true"/>

配置好后@Transactional就可以正常回滚了。

  1. Mysql事务中到底怎么完成回滚?
    调试con.rollback()方法中,可以看到调用了以下方法:
private void rollbackNoChecks() throws SQLException {
    try {        
        synchronized(this.getConnectionMutex()) {
            if (!(Boolean)this.useLocalTransactionState.getValue() || this.session.getServerSession().inTransactionOnServer()) {
                this.session.execSQL((Query)null, "rollback", -1, (NativePacketPayload)null, false, this.nullStatementResultSetFactory, this.database, (ColumnDefinition)null, false);
            }
        }
    } catch (CJException var5) {
        throw SQLExceptionsMapping.translateException(var5, this.getExceptionInterceptor());
    }
}
  1. 方法中执行了execSQL((Query)null, “rollback”,***),去完成了回滚操作。

结论

  1. @Transactional、TransactionComponent、TransactionTemplate均可以完成在Mysql的INNODB存储引擎上的事务回滚,但不能在默认的MyISAM存储引擎完成回滚。
  2. TransactionComponent、TransactionTemplate使用方法类似,需要手动使用代码控制事务。@Transactional注解在开发上一般使用在实现类的方法上,使用方便,代码量少,粒度较显式使用会粗一点。
  3. 三种方法均需配置ResourceTransactionManager的Bean交于Spring管理事务。
  4. 开发时导出数据库建表语句建议直接显式声明其存储引擎,以防有些数据库并未设置默认引擎为INNODB。
    (修改数据库默认存储引擎:在配置文件my.cnf中的 [mysqld] 下面加入default-storage-engine=INNODB)
default-storage-engine=INNODB
  1. 可以在数据库执行sql show engines查看默认的存储引擎:

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1月前
|
SQL 监控 关系型数据库
MySQL怎么全局把一张表的数据回滚
MySQL怎么全局把一张表的数据回滚
156 2
|
3月前
|
SQL 关系型数据库 MySQL
【MySQL】根据binlog日志获取回滚sql的一个开发思路
【MySQL】根据binlog日志获取回滚sql的一个开发思路
|
1月前
|
存储 SQL 关系型数据库
MySQL的事务隔离级别
【10月更文挑战第17天】MySQL的事务隔离级别
98 43
|
27天前
|
SQL 关系型数据库 MySQL
mysql数据误删后的数据回滚
【11月更文挑战第1天】本文介绍了四种恢复误删数据的方法:1. 使用事务回滚,通过 `pymysql` 库在 Python 中实现;2. 使用备份恢复,通过 `mysqldump` 命令备份和恢复数据;3. 使用二进制日志恢复,通过 `mysqlbinlog` 工具恢复特定位置的事件;4. 使用延迟复制从副本恢复,通过停止和重启从库复制来恢复数据。每种方法都有详细的步骤和示例代码。
136 2
|
1月前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1645 14
|
2月前
|
存储 Oracle 关系型数据库
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
从基本特性、技术选型、字段类型、事务提交方式、SQL语句、分页方法等方面对比Oracle和MySQL的区别。
544 18
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
|
1月前
|
SQL 关系型数据库 MySQL
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
尼恩,一位40岁的资深架构师,通过其丰富的经验和深厚的技術功底,为众多读者提供了宝贵的面试指导和技术分享。在他的读者交流群中,许多小伙伴获得了来自一线互联网企业的面试机会,并成功应对了诸如事务ACID特性实现、MVCC等相关面试题。尼恩特别整理了这些常见面试题的系统化解答,形成了《MVCC 学习圣经:一次穿透MYSQL MVCC》PDF文档,旨在帮助大家在面试中展示出扎实的技术功底,提高面试成功率。此外,他还编写了《尼恩Java面试宝典》等资料,涵盖了大量面试题和答案,帮助读者全面提升技术面试的表现。这些资料不仅内容详实,而且持续更新,是求职者备战技术面试的宝贵资源。
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
|
2月前
|
SQL 关系型数据库 MySQL
MySQL基础:事务
本文详细介绍了数据库事务的概念及操作,包括事务的定义、开启、提交与回滚。事务作为一组不可分割的操作集合,确保了数据的一致性和完整性。文章还探讨了事务的四大特性(原子性、一致性、隔离性、持久性),并分析了并发事务可能引发的问题及其解决方案,如脏读、不可重复读和幻读。最后,详细讲解了不同事务隔离级别的特点和应用场景。
147 4
MySQL基础:事务
|
2月前
|
SQL 监控 关系型数据库
MySQL数据库中如何检查一条SQL语句是否被回滚
检查MySQL中的SQL语句是否被回滚需要综合使用日志分析、事务状态监控和事务控制语句。理解和应用这些工具和命令,可以有效地管理和验证数据库事务的执行情况,确保数据的一致性和系统的稳定性。此外,熟悉事务的ACID属性和正确设置事务隔离级别对于预防数据问题和解决事务冲突同样重要。
84 2
|
1月前
|
SQL 关系型数据库 MySQL
【MySQL】索引和事务
【MySQL】索引和事务
55 0

热门文章

最新文章

下一篇
无影云桌面