Read Uncommited
在RU模式下,即使事务没有commit,在其他事务中仍可以读到未提交的数据。
RU是所有隔离级别中最低的一种。RU模式会导致脏读
Read Commited
RC模式下,事务只能读取到已经commit的数据。
比如事务a在执行时,如果事务b没有提交,a是读不到b的数据的。如果b提交a便能读到b修改的数据。
RC可以避免脏读,但是会导致不可重复读。
大部分系统使用的是RC模式
Repeatable
RR模式下,事务的多次Read不会受其他事务的影响(无论提交与否)
可以理解为RR模式下事务a在创建的时候获取了一次当前时刻数据的快照,快照不受其余事务的影响。
RR模式可能会导致幻读,因为无法感知其余事务,可能导致重复的插入。
mysql innoDB的RR模式可以一定程度避免幻读,该特性是通过间隙锁(gap lock)来实现的,间隙锁的原理可以参照这篇文章http://www.jianshu.com/p/bf86...
Serializable
串行化,顾名思义,是将所有读写操作完全串行。
串行化是所有隔离级别中最高的
每次读都需要获得表级共享锁,读写相互都会阻塞
串行化对资源的开销大,对并发支持不好,只在,某些场景下使用。
最后给出四个隔离级别对三种读类型的对照表:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read Uncommited | 可能 | 可能 | 可能 |
Read Commited | 不可能 | 可能 | 可能 |
Repeatable Read | 不可能 | 不可能 | 可能 |
Serializable | 不可能 | 不可能 | 不可能 |
实验
read uncommitted
1.创建表
MySQL [test]> create table `transaction_test`(`id` int(11) not null auto_increment,`num` int(11) not null default '0', primary key (`id`))engine=InnoDB;
Query OK, 0 rows affected (0.02 sec)
2.对A事务,设置隔离级别为未提交读
MySQL [test]> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
MySQL [test]> select @@tx_isolation;
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set (0.00 sec)
3.开启A事务,查表
MySQL [test]> start transaction;
Query OK, 0 rows affected (0.00 sec)
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
3.开启B事务
MySQL [test]> start transaction;
Query OK, 0 rows affected (0.00 sec)
4.事务B更新id=1的num
MySQL [test]> update transaction_test set num='10' where id='1';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 10 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
5.B事务未提交,A事务再次查询,发现id=1的行被修改
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 10 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
这就是所谓的脏读;
经过上面的实验可以得出结论,事务B更新了一条记录,但是没有提交,此时事务A可以查询出未提交记录。造成脏读现象。未提交读是最低的隔离级别。
read committed
1.设置A的隔离级别为RC,开启事务
MySQL [test]> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
MySQL [test]> start transaction;
Query OK, 0 rows affected (0.00 sec)
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
2.B开启事务,更新id=1的行,但不提交事务
MySQL [test]> start transaction;
Query OK, 0 rows affected (0.00 sec)
MySQL [test]> update transaction_test set num='10' where id='1';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 10 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
3.因为B事务未提交,所以A再次select,不会查询到变化的数据
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
4.B提交事务
MySQL [test]> commit;
Query OK, 0 rows affected (0.01 sec)
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 10 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
5.A再次select,发现数据已发生变化,说明B提交的修改被事务中的A读到了,这就是所谓的不可重复读
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 10 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
repeatable read
1.将A的隔离级别设为repeatable read
MySQL [test]> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
2.B开启事务,更新id=1的行并提交事务
MySQL [test]> start transaction;
Query OK, 0 rows affected (0.00 sec)
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
MySQL [test]> update transaction_test set num='10' where id='1';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MySQL [test]> commit;
Query OK, 0 rows affected (0.00 sec)
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 10 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
3.A再次查询
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
发现数据依然未发生变化,这说明这次可以重复读了
4.重新将id=1的num改为1,并开启A事务;
MySQL [test]> update transaction_test set num='1' where id='1';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.01 sec)
MySQL [test]> start transaction;
Query OK, 0 rows affected (0.00 sec)
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
2.开启B事务,插入一行,id=4,不提交
MySQL [test]> start transaction;
Query OK, 0 rows affected (0.00 sec)
MySQL [test]> insert into transaction_test values('4', '4');
Query OK, 1 row affected (0.00 sec)
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
+----+-----+
4 rows in set (0.00 sec)
3.A查询,查不到id=4的记录
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
4.A插入相同的记录
MySQL [test]> insert into transaction_test values('4', '4');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
会一直阻塞,无法插入;
5.B事务提交刚刚的插入
MySQL [test]> commit;
Query OK, 0 rows affected (0.00 sec)
6.A事务再次插入
MySQL [test]> insert into transaction_test values('4', '4');
ERROR 1062 (23000): Duplicate entry '4' for key 'PRIMARY'
7.A事务查询
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
报了主键冲突,这里事务A就是发生了幻读,因为事务A读取的数据状态并不能支持它的下一步的业务
serializable
1.删掉上一步插入的id=4的行,设置隔离级别为serializable
MySQL [test]> delete from transaction_test where id=4;
Query OK, 1 row affected (0.01 sec)
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
MySQL [test]> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE |
+----------------+
1 row in set (0.00 sec)
2.开启事务A,事务B
MySQL [test]> start transaction;
Query OK, 0 rows affected (0.00 sec)
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
MySQL [test]> start transaction;
Query OK, 0 rows affected (0.00 sec)
MySQL [test]> select * from transaction_test;
+----+-----+
| id | num |
+----+-----+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-----+
3 rows in set (0.00 sec)
3.事务B插入id=4的行
MySQL [test]> insert into transaction_test values('4', '4');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
若事务A不提交,会一直阻塞,直至超时
4.事务A提交
MySQL [test]> commit;
Query OK, 0 rows affected (0.00 sec)
5.事务B插入成功
MySQL [test]> insert into transaction_test values('4', '4');
Query OK, 1 row affected (4.78 sec)
SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面
说的幻读的问题。
简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。
参考文章
https://segmentfault.com/a/11...
高性能mysql 1.3.1章
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。