MYSQL事务的四种隔离级别简介
读未提交
其他事务未提交的数据变更对本事务可见。
读已提交
其他事务未提交的数据变更对本事务不可见。
可重复读
在一次事务里,本事务对某行记录多次读取的结果是一致的,即使中间有其他事务提交了对该数据的变更。
序列化
多个事务之间必须串行执行,事务之间的操作不可交叉。
——————————————————————
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
——————————————————————
|读未提交 | Y | Y | Y |
——————————————————————
|读已提交 | X | Y | Y |
——————————————————————
|可重复读 | X | X | Y |
——————————————————————
|序列化 | X | X | X |
测试
测试数据准备
CREATE TABLE `People` (
`ID` bigint(20) NOT NULL,
`Name` varchar(255) NOT NULL,
`Age` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `People` (`ID`, `Name`, `Age`)
VALUES (1, '炒饭', 31),(2, '阿丹', 33)
脏读
事务1 set session transaction isolation level read uncommitted
事务1 start transaction
事务2 start transaction
事务2 update People set Age=32 where ID=1;
事务1 select * from People where ID=1; 结果

事务1读到了事务2未提交的数据变更
解决脏读需要事务1设置隔离级别为读提交read committed
set session transaction isolation level read committed
不可重复读
事务1 set session transaction isolation level read committed
事务1 start transaction
事务2 start transaction
事务1 select * from People where ID=1; 结果

事务2 update People set Age=33 where ID=1;
事务2 commit;
事务1 select * from People where ID=1; 结果

事务1第一次查询ID=1的记录跟第二次查询结果不一样,称为不可重复读
解决不可重复读需要设置隔离级别为可重复读
set session transaction isolation level read repeatable read
幻读
幻读,并不是说两次读取获取的结果集不同(可重复读级别已经解决该问题),幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。(很多人对幻读有误解,个人认同这个作者的观点 https://segmentfault.com/a/1190000016566788)
事务1 set session transaction isolation level repeatable read
事务1 start transaction
事务2 start transaction
事务1 select * from People where ID=1
事务2 insert into People (ID,Name,Age) values(1,'西西',15)
事务1 如果select记录不存在,执行insert into People (ID,Name,Age) values(1,'西西',15)
事务2 commit;
事务1 commit;
事务1希望ID=1的记录不存在时去执行插入操作,然而事务2先插入了记录,事务1又插入了一次记录,仿佛之前事务1的select看到的跟幻觉一样。
在可重复读隔离级别下,避免幻读,可以用select * from People where ID=1 for update,加上行锁,能保证当前事务不受其他事务的写入干扰。
网友评论