MySQL 事务浅析

2022-02-18 00:00:00 执行 事务 提交 隔离 级别

我们是否考虑过一个问题:转账会不会出现钱扣了对方也没收到钱的情况?按照现在的技术,基本也不会出现,因为目前一个转账操作基本在一个事务中。事务内的语句,要么全部执行成功,要么全部执行失败。也就是说,上面转账过程中,即使中间出现问题,也会回滚,取消扣钱操作。

那 MySQL 事务还有哪些特性呢?这一节内容就来聊聊。

1 事务控制

开始一个事务

begin;

或者

start transaction;

提交事务:

commit;

回滚事务

rollback;

参数 autocommit 可以控制当前会话是否自动提交,如果值为 1,则表示自动提交,每次执行事务不需要我们执行 begin 或者 start transaction。


如果全局为自动提交,但是想要部分 SQL 禁用自动提交,则使用下面的方法:

start transaction;update table1 set a=1 where id=1;update table2 set a=2 where id=1;commit;


2 ACID 特性

一个运行良好的事务处理系统,必须具备 ACID 特性:

  •  A:  atomicity(原子性)  :要么全执行,要么全都不执行

  •  C:  consistency(一致性):在事务开始和完成时,数据都必须保持一致状态

  •  I:  isolation(隔离性)   :事务处理过程中的中间状态对外部是不可见的。

  •  D:  durability(持久性)  :事务完成之后,它对于数据的修改是性的。

至于 MySQL 是如何来保证这些特性,我们在后面的文章详细介绍。


3 事务隔离级别

MySQL 有四种隔离级别:

  • READ UNCOMMITTED(读未提交,RU):在该隔离级别,所有事务都可以看到其它未提交的事务的执行结果。可能会出现脏读。

  • READ COMMITTED(读已提交,RC):一个事务只能看见已经提交事务所做的改变。因为同一事务的其它实例在该实例处理期间可能会有新的 commit,所以可能出现幻读。

  • REPEATABLE READ(可重复读,RR):这是 MySQL 的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。消除了脏读、不可重复读,默认也不会出现幻读。

  • SERIALIZABLE(串行):这是高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。一般不建议使用。


如何选择合适的隔离级别:

  • 对于 RU 隔离级别,会导致脏读,因此生产环境不建议使用。

  • 对于 RC 隔离级别,相比 RU 隔离级别,不会出现脏读;但是会出现幻读,一个事务中的两次执行同样的查询,可能得到不一样的结果。

  • 对于 RR 隔离级别,相比 RC 隔离级别,解决了部分幻读,但是相对于 RC,锁的范围可能更大了。

  • 对于 Serializable 隔离级别,因为它强制事务串行执行,会在读取的每一行数据上都加锁,因此可能会导致大量的超时和锁争用的问题。生成环境不建议使用。

  • 生产环境一般选择 RC 或者 RR。


4 有关 RC 和 RR 的对比实验

执行下面 SQL,进行实验前准备:

use martin;
drop procedure if exists insert_t1; delimiter ;;create procedure insert_t1() begin
drop table if exists t1;
CREATE TABLE `t1` (`id` int(11) NOT NULL AUTO_INCREMENT,`a` int(11) NOT NULL,`b` int(11) NOT NULL,PRIMARY KEY (`id`),KEY `idx_c` (`a`)) ENGINE=InnoDB CHARSET=utf8mb4;insert into t1(a,b) values (1,1),(2,2);
end;;delimiter ;

4.1 Read Committed 实验

执行初始化语句:

call insert_t1();

进行实验:

session1 session2
set session transaction_isolation='READ-COMMITTED'; set session transaction_isolation='READ-COMMITTED';
begin; begin;

select * from t1 where a=1;



insert into t1(a,b) values (1,3);
select * from t1 where a=1;



commit;
select * from t1 where a=1;


commit;

session2 写入了新数据未提交的情况下,session1 无法查看到新记录,等到 session2 提交之后,session1 才能看到 session2 写入的数据。出现了一个事务中的两次执行同样的查询,得到不一样的结果,也就是幻读。


4.2 Repeatable Read 实验

执行初始化语句

call insert_t1();

进行实验

session1 session2
set session transaction_isolation='REPEATABLE-READ'; set session transaction_isolation='REPEATABLE-READ';
begin; begin;
select * from t1 where a=1;



insert into t1(a,b) values (1,3);
select * from t1 where a=1;



commit;
select * from t1 where a=1;


commit;
select * from t1 where a=1;


session2 写入了新数据未提交的情况下,session1 无法查看到新记录,等到 session2 提交但是 session1 还未提交时,session1 还是不能看到新记录,没有出现 RC 隔离级别实验的幻读现象。需要等 session1 事务提交之后,才能查看到 session2 写入的新数据。


以上文章来源于公众号悦专栏 ,作者马听

相关文章