一、隔离级别导致的问题
- 脏读:A事物允许读取到B事物未提交的数据。
- 不可重复读:A事物在时间点T1读取了一些记录,在T2时再想重新读取一次同样的这些记录时,这些记录可能已经被改变、或者消失不见。
- 幻读:A事物在任意时刻查询的结果都是事务开始时的状态(一致性)。但是,如果另一个事务同时提交了新数据,本事务再更新时,就会“惊奇的”发现了这些新数据,貌似之前读到的数据是“鬼影”一样的幻觉。
二、事物隔离级别
- Read Uncommitted
- Read Committed
- Repeatable Read
- Serializable
每种隔离级别产生的问题:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
Read Uncommitted | Y | Y | Y |
Read Committed | N | Y | Y |
Repeatable Read(MySQL默认事物隔离级别) | N | N | Y |
Serializable
| N | N | N |
三、测试
1.首先检查一下MySQL的版本号
select version();
2.查看一下系统的隔离级别和回话隔离级别
select @@global.tx_isolation, @@tx_isolation;
3.修改当前会话隔离级别,方便做测试
set session tx_isolation='read-uncommitted';
select @@global.tx_isolation, @@tx_isolation;
可以发现测试会话的隔离级别变成了不可重复读
4.修改系统级别的隔离级别
set global tx_isolation='read-uncommitted';
select @@global.tx_isolation, @@tx_isolation;
此时系统级和会话级别的事物隔离级别都变成了:READ-UNCOMMITTED
5.关闭SQL自动提交功能
set autocommit=off;
show variables like 'autocommit';
6.建立测试表
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
7.测试脏读
Transaction A | Transaction B |
start transaction; | |
start transaction; insert into user(name, age) values ('张三',20); | |
此时Transaction B尚未提交,但是Transaction A已经读取到B更新的数据了 | |
rollback; | |
此时Transaction B回滚了,但是Transaction A已经读取不到刚刚那条记录了 | |
8.测试不可重复读
set global tx_isolation='read-committed';
set session tx_isolation='read-committed';
Transaction A | Transaction B |
start transaction; | |
start transaction; insert into user(name, age) values ('李四',30); | |
TransactionB未提交,所以TransactionA读取不到B的修改 | |
commit; | |
TransactionB提交,所以TransactionA读取到B的修改 |
9.测试可重复读
set global tx_isolation='repeatable-read';
set session tx_isolation='repeatable-read';
修改隔离级别为可重复读,这样同一个事物Transaction A中任一时刻读取的数据是可以保持一致了,但是紧接着就带来了幻读的问题了。
Transaction A | Transaction B |
start transaction; insert into user(id, name, age) values (6,'麻子',35); commit; | |
TransactionB提交,但是此时TransactionA没有读取到B的修改,这就是所谓可重复读 | |
------------------------------------------------------华丽的分割线------------------------------------------------------------ 至此验证完了可重复读,下面紧接着就出现了幻读~ | |
此时在TransactionA中执行insert into user(id, name, age) values (6,'麻子',35);出现错误 但是在华丽的分割线的上面,查询语句是没有查到有id为6的记录的 | |
10.测试Serializable
set global tx_isolation='serializable';
set session tx_isolation='serializable';
设置隔离级别为Serializable后,就不会出现幻读的问题了。
在这种情况下,只允许一个事务在执行,其它事务必须等待这个事务执行完后才能执行。没有并发,只是单纯的串行。