MySQL事务理论及实现

理论大多引自《高性能MySQL》一书,不过在自测的过程中不知道是不是SQL版本的问题,还是操作有问题,在设置事务隔离级别的时候 按书上讲SET TRANSACTION ISOLATION LEVEL 这样设置并没有实现

代码实现事务回滚,基于spring boot
1、将MySQL的自动提交关掉(不是必须的)。

SET SESSION autocommit=0; 只对当前会话生效
SET autocommit = 0; 只对本次连接生效,当断开本次与MySQL的连接,则又回到默认的自动提交模式
SET GLOBAL autocommit = 0 更改系统变量,但是如果MySQL重启则又回到默认的自动提交模式

修改配置文件永久生效
修改etc/my.cnf 文件
在[mysqld]下添加如下,记得重启。
autocommit=0

2、添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

3、在启动类上添加注解

@EnableTransactionManagement

4、在service层添加注解(加在类名上对该类下所有方法生效,加在方法上只对该方法生效),对抛出的任何异常都进行自动回滚

	@Transactional(rollbackFor = Exception.class)
	public boolean insertBook() {
	    int i = 0;
	    i += bookMapper.insertBook();
	    i += bookMapper.insertUser();
	    if (i == 2) {
	        return true;
	    } else {
	        return false;
	    }
	}

对于回滚失败的问题,整理以下问题

(1)MySQL默认存储引擎是InnoDB是支持事务的,你可能采用了不支持事务的引擎。
(2)如果异常被try{} catch{}了,你自己把异常处理了spring就不回滚了,如果想让事务回滚,必须手动再抛出异常,或者手动回滚

	TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

(3)如果try-catch语句在finally中进行了return操作,那么catch中手动抛出的异常也会被覆盖,同样不会自动回滚

	@Transactional(rollbackFor = Exception.class)
	public boolean insertBook() {
	    int i = 0;
	    i += bookMapper.insertBook();
	
	    try {
	        i += bookMapper.insertUser();
	    } catch (Exception e) {
	        throw new Exception();
	    } finally {
	        return true;
	    }
	    //TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
	 /*  if (i == 2) {
	        return true;
	    } else {
	        return false;
	    }*/
	}

(4)两个方法A和B,A方法调用了B方法,A没有设置事务,B设置了,此时对于A来讲也不会发生回滚
(5)标记@Transactional的方法必须是public 其他类型都不会开启事务
(6)如果用到了shiro框架,需要对ShrioRealm中注入的service 加注解 @Lazy或者直接调用mapper,否则对该service层事务也是不起作用的

事务简介

事务就是一组原子性的SQL查询,简单说就是开启事务后,你所执行的一组SQL语句,如果其中有任何一条语句
因为崩溃或其他原因执行失败,那么所有语句都不会执行。要么全部执行成功,要么全部失败。

开启事务
START TRANSACTION;或者BEGIN;
开启事务后要么使用 COMMIT 提交事务将修改的数据持久保留,要么使用ROLLBACK回滚所有的修改

格式如下:
START TRANSACTION;
-- BEGIN;
SELECT * FROM booktype;
UPDATE booktype SET bookTypeName = '计算机类' WHERE id = 1
-- ROLLBACK;
COMMIT;

单纯的事务概念并不是故事的全部,如果在事务执行过程中因为服务器崩溃怎么办?所以一个运行良好的事务处理系统必须满足ACID标准。

原子性(atomicity)
一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分。这就是事务的原子性。

一致性(consistency)
数据库总是从一个一致性状态转换到另一个一致性的状态。只要事务最终没有提交,事务中所作的修改也不会保存到数据库中。

隔离性(isolation)
简单来说就是一个事务所作的修改在最终提交前,对其他事务是不可见的。

持久性(durability)
一旦事务提交,则其所作的修改就会永久保存到数据库中,此时即使系统崩溃,修改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。而且不可能有做到100%持久性保证的策略(如果数据库本身就能做到真正的持久性,那么备份又怎么能增加持久性呢?)

1、事务四种隔离级别
READ UNCOMMITTED(读未提交)
事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read)。实际应用中很少使用。

READ COMMITTED(读已提交)
一个事务开始时,只能看到已经提交的事务所作的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别也叫做不可重复读,因为两次执行同样的查询,可能会得到不一样的结果。

REPEATABLE READ(可重复读)
MySQL默认的事务隔离级别。解决了脏读的问题。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。但是理论上,可重复读还是无法解决另外一个幻读的问题。所谓幻读,指的是当某个事务再次读取某个范围的数据时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。MySQL默认的存储引擎是InnoDB,通过多版本并发控制解决了幻读的问题。

SERIALIZABLE(可串行化)
事务隔离级别的最高级别,它通过强制事务串行执行,避免了前面说的幻读的问题。简单说就是SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁的争用问题。实际开发中也很少用这个隔离级别。

MySQL查询当前系统的隔离级别
SHOW GLOBAL VARIABLES LIKE ‘%ISOLATION%’;
查询当前会话的隔离级别:SELECT @@tx_isolation;
在这里插入图片描述MySQL可以通过SET GLOBAL TRANSACTION ISOLATION LEVEL 命令来设置系统隔离级别。新的隔离级别会在下一个事务中开始时生效。可以在配置文件中设置整个数据库的隔离级别,也可以指该表当前会话的隔离级别:SET SESSION TRANSACTION ISOLATION LEVEL

配置文件修改隔离级别
在mysql配置文件my.cnf中[mysqld]段下加上transaction-isolation=Read-Committed
然后重启

2、死锁
死锁是指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶行循环的现象。当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁。多个事务同时锁定同一个资源时,也会产生死锁。

MySQL默认的存储引擎InnoDB目前处理死锁的方法是,将持有最少行级排他锁的事务进行回滚(这是相对比较简单的死锁回滚算法)。

死锁发生以后,只有部分或者完全回滚其中一个事务,才能打破死锁。大多数情况下只需要重新执行因死锁回滚的事务即可。

3、MySQL中的事务
MySQL默认采用自动提交的模式(AUTOCOMMIT)。也就是说如果不是显示的开始一个事务,则每个查询都被当作一个 事务执行提交操作。在当前连接中,可以通过设置AUTOCOMMIT变量来启用或者禁用自动提交模式。
SHOW VARIABLES LIKE AUTOCOMMIT
在这里插入图片描述
SET AUTOCOMMIT= 1;
1或ON表示启用,0或OFF表示禁用。
当 AUTOCOMMIT= 0时,所有的查询都是在一个事务中,知道显式的执行COMMIT提交或者ROLLBACK回滚,该事务结束,同时又开始了另一个新事务。修改AUTOCOMMIT对非事务型的表,如MyISAM或者内存表,不会有任何影响。对这类表来说,没有COMMIT或者ROLLBACK的概念,也可以说是相当于一直处于AUTOCOMMIT启用的模式。

另外还有一些命令,在执行之前会强制执行COMMIT提交当前的活动事务。典型的在数据定义语言(DDL)中,如果是会导致大量数据改变的操作,比如ALTER TABLE就是如此。

InnoDB采用的是两阶段锁定协议

在事务执行过程中,随时都可以执行锁定,锁只有在执行COMMIT或者ROLLBACK的时候才会释放,并且所有的锁是在同一时刻被释放。前面描述的都是隐式锁定,InnoDB会根据隔离级别在需要的时候自动枷锁。如果应用需要用到事务,还是应该选择事务型存储引擎。

posted @ 2022-01-20 23:22  暮雨寒冬  阅读(65)  评论(0编辑  收藏  举报