概念
当数据库上有多个事务同时执行的时候,就可能出现
脏读(dirty read)
、不可重复读(non-repeatable read)
、幻读(phantom read)
的问题,为了解决这些问题,就有了"隔离级别"的概念。
隔离得越严实,效率就会越低。要在二者之间寻找一个平衡点。
隔离级别
读未提交
一个事务还没提交时,它做的变更就能被别的事务看到。
读已提交
一个事务提交之后,它做的变更才会被其他事务看到。
可重复读
一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
串行化
顾名思义是对于同一行记录,
写
会加写锁
,读
会加读锁
。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
命令
查看
mysql> select @@GLOBAL.tx_isolation,@@session.tx_isolation;
+-----------------------+------------------------+
| @@GLOBAL.tx_isolation | @@session.tx_isolation |
+-----------------------+------------------------+
| REPEATABLE-READ | REPEATABLE-READ |
+-----------------------+------------------------+
1 row in set, 2 warnings (0.10 sec)
设置
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
case
mysql> create table T(c int) engine=InnoDB;
insert into T(c) values(1);
事务 A | 事务 B |
---|---|
启动事务;查询得到值 1 | 启动事务 |
查询得到值 1 | |
将 1 改成 2 | |
查询得到值 V1 | |
提交事务 | |
查询得到值 V2 | |
提交事务 | |
查询得到值 V3 |
-
若隔离级别是
读未提交
, 则V1
的值就是 2 。这时候事务B
虽然还没有提交,但是结果已经被事务A
看到了。因此,V2
、V3
也都是 2 。 -
若隔离级别是
读提交
,则V1
是 1 ,V2
的值是 2 。事务B
的更新在提交后才能被事务A
看到。所以,V3
的值也是 2 。 -
若隔离级别是
可重复读
,则V1
、V2
是 1 ,V3
是 2 。之所以V2
还是 1 ,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。 -
若隔离级别是
串行化
,则在事务B
执行将 1 改成 2 的时候,会被锁住
。直到事务A
提交后,事务B
才可以继续执行。所以从事务A
的角度看,V1
、V2
值是 1 ,V3
的值是 2 。
数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。在
可重复读
隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。在读提交
隔离级别下,这个视图是在每个 SQL 语句开始执行的时候创建的。读未提交
隔离级别下直接返回记录上的最新值,没有视图概念。而串行化
隔离级别下直接用加锁的方式来避免并行访问。
Oracle 数据库的默认隔离级别其实就是
读提交
,因此对于一些从 Oracle 迁移到 MySQL 的应用,为保证数据库隔离级别的一致,一定要记得将 MySQL 的隔离级别设置为读提交
。
并发问题
脏读
:对于两个事务T1
,T2
,T1
读取了已经被T2更新
但还没有被提交
的字段. 之后, 若T2 回滚
,T1读取
的内容就是临时且无效的
不可重复读
:对于两个事务T1
,T2
,T1
读取了一个字段, 然后T2 更新
了该字段提交
. 之后,T1
再次读取
同一个字段, 值就不同了(在一个事物中不管读多少次,读取的数据应该都一样)幻读
:对于两个事务T1
,T2
,T1
从一个表中读取
了一个字段, 然后T2
在该表中插入
了一些新的行. 之后, 如果T1
再次读取同一个表, 就会多出几行
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
读未提交 | √ | √ | √ |
读已提交 | × | √ | √ |
可重复读 | × | × | √ |
串行化 | × | × | × |