MVCC原理(多版本并发控制)
说明
Innodb通过在读取的时刻建立快照,来保证一个事务中的读取一致性。
- 在该时刻之前的数据是可以查询到的
- 在该时刻之后的数据是查询不到的
- 有一个例外需要注意,如果事务修改了该时刻后面的数据,那么当前事务在查询时就会读取到该条数据
演示一
事务A | 事务B |
---|---|
mysql> set autocommit = 0; Query OK, 0 rows affected (0.00 sec) |
mysql> set autocommit = 0; Query OK, 0 rows affected (0.00 sec) |
mysql> select * from user; Empty set (0.00 sec) |
|
mysql> insert into user values(1,'fc'); Query OK, 1 row affected (0.00 sec) |
|
mysql> select * from user; +----+———+ | id | name | +----+———+ | 1 | fc | |
|
mysql> select * from user; Empty set (0.00 sec) |
结论:从第一个查询操作开始建立快照,该快照应用于整个事务中。
演示二
事务A | 事务B |
---|---|
mysql> set autocommit = 0; Query OK, 0 rows affected (0.00 sec) |
mysql> set autocommit = 0; Query OK, 0 rows affected (0.00 sec) |
mysql> select * from user; Empty set (0.00 sec) |
mysql> select * from user; Empty set (0.00 sec) |
mysql> insert into user values(2,'tt'); Query OK, 1 row affected (0.00 sec) |
|
mysql> select * from user; +----+———+ | id | name | +----+———+ | 1 | fc | +----+------+ 1 row in set (0.00 sec) |
|
mysql> select * from user; Empty set (0.00 sec) |
|
mysql> update user set name = 'xtt' where id = 2; 注意此时事务B的新增语句并没有提交,这里会一直阻塞,等待事务B的提交 |
|
mysql> commit; | |
Query OK, 1 row affected (29.31 sec) Rows matched: 1 Changed: 1 Warnings: 0 |
|
mysql> select * from user; +----+------+ | id | name | +----+------+ | 2 | xtt | +----+------+ 1 row in set (0.00 sec) |
总结:如果当前事务A修改其他事务B新增未提交的数据,那么会出现阻塞的情况,直到事务B提交事务。那么如果事务B又将该数据删除了呢,或者事务B是修改了未提交呢?
演示三
事务A | 事务B |
---|---|
mysql> set autocommit = 0; Query OK, 0 rows affected (0.00 sec) |
mysql> set autocommit = 0; Query OK, 0 rows affected (0.00 sec) |
mysql> select * from user; +----+———+ | id | name | +----+———+ | 2 | tt | +----+------+ 1 row in set (0.00 sec) |
|
mysql> update user set name = 'niufuren' where id =2; Query OK, 1 row affected (0.00 sec) |
|
mysql> update user set name = 'tt' where id = 2; 注意此时事务B的新增语句并没有提交,这里会一直阻塞,等待事务B的提交 |
|
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 等待超时 | mysql> commit; Query OK, 0 rows affected (0.00 sec) |
mysql> select * from user; +----+------+ | id | name | +----+------+ | 2 | xtt | +----+------+ 1 row in set (0.00 sec) 这里一致性读,依然读的快照 |
总结:如果事务B又将该数据删除了呢,或者事务B是修改了未提交,事务A再去删除或者修改该数据时都会阻塞,但是事务A是可以正常读取的,也就是可重复读。
快照的创建
-
RR隔离级别下:当事务中第一个需要读取的操作时创建快照,也可以通过命令在事务开始时就创建快照
-
通用select开启事务
-
通过start transaction with consistent snapshot;
-
-
RC隔离界别下:事务中每一个一致性读的操作都会建立自己的新快照
一致性读不适用与DDL语句
- 当事务A在事务中已经建立了user表的快照是后,其他事务是无法对user表进行DDL的,DDL是直接生效的没有经过事务提交
- 在RR和RC隔离界别下,一致性读是不会对行加锁的
参考:
https://dev.mysql.com/doc/refman/5.6/en/innodb-consistent-read.html