我们是否考虑过一个问题:转账会不会出现钱扣了对方也没收到钱的情况?按照现在的技术,基本也不会出现,因为目前一个转账操作基本在一个事务中。事务内的语句,要么全部执行成功,要么全部执行失败。也就是说,上面转账过程中,即使中间出现问题,也会回滚,取消扣钱操作。
那 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 写入的新数据。
以上文章来源于公众号悦专栏 ,作者马听