目录
一、Innodb事务原理
1、什么是事务
- 事务(Transaction)是数据库区别于文件系统的重要特性之一,事务会把数据库从一种一致性状态转换成为另一种一致性状态。
- 在数据库提交时,可以确保要么所有修改都已保存,要么所有修改都不保存。
2、事务的特性:(ACID)
- 原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行。
- 一致性(Consistency):几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果一致。
- 隔离性(Isolation):事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的。
- 持久性(Durability):对于任意已提交的事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。
3、事务的分类
3.1、扁平事务(Flat Transactions)
- 扁平事务是事务类型中最简单但使用最频繁的事务。
- 在扁平事务中,所有的操作都处于同一层次,由begin/start transaction开始事务,由commit/rollback结束,且都是原子性的,要么都执行,要么都回滚。
- 扁平事务是应用程序成为原子操作的基本组成模块。
扁平事务一般有四种不同的效果:
- 事务成功完成
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into student(name) value('wangwu');
Query OK, 1 row affected (0.00 sec)
mysql> update student set name = '王五' where id = 4;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from student;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | wangwu |
| 4 | 王五 |
+----+--------+
4 rows in set (0.00 sec)
- 应用程序要求停止事务。比如应用程序在捕获到异常时会回滚事务
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into student(name) value('joke');
Query OK, 1 row affected (0.02 sec)
mysql> select * from student;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | wangwu |
| 4 | 王五 |
| 5 | joke |
+----+--------+
5 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.07 sec)
mysql> select * from student;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | wangwu |
| 4 | 王五 |
+----+--------+
4 rows in set (0.00 sec)
- 外界因素强制终止事务。如连接超时或连接断开
mysql> begin;
Query OK, 0 rows affected (0.07 sec)
mysql> insert into student(name) value('田七');
Query OK, 1 row affected (0.01 sec)
mysql> # 此时,我将MySQL的服务停止掉了,去执行删除操作
mysql> delete from student where id=7;
ERROR 2013 (HY000): Lost connection to MySQL server during query
msyql> # 此时,我将MySQL的服务重新启动,去执行删除操作
mysql> delete from student where id=7;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id: 3
Current database: mytest
Query OK, 0 rows affected (0.03 sec)
mysql> select * from student;
+----+--------+
| id | name |
+----+--------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | wangwu |
| 4 | 王五 |
| 6 | 赵六 |
+----+--------+
5 rows in set (0.00 sec)
- 带有保存节点的扁平事务
-
带有保存节点的扁平事务允许事务在执行过程中回滚到较早的一个状态,而不是回滚所有的操作。
-
保存点用来通知系统应该记住事务当前的状态,以便当之后发生错误时,事务能回到保存点当时的状态。
-
对于扁平事务来说,在事务开始时隐式地设置了一个保存点,回滚时只能回滚到事务开始时的状态。
#带有保存节点的扁平事务
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from student;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | 张三 | 18 |
| 2 | 李四 | 19 |
+----+------+-----+
2 rows in set (0.00 sec)
mysql> update student set age=28 where id=1;
Query OK, 1 row affected (0.04 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> savepoint sp1;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into student(name,age) value ('王五',20);
Query OK, 1 row affected (0.00 sec)
mysql> rollback to sp1;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from student;
+----+------+-----+
| id | name | age |
+----+------+-----+
| 1 | 张三 | 28 |
| 2 | 李四 | 19 |
+----+------+-----+
2 rows in set (0.00 sec)
3.2、链事务
什么是链事务
-
链事务(Chained Transaction)是指一个事务由多个子事务链式组成
-
前一个子事务的提交操作和下一个子事务的开始操作合并成一个原子操作,这意味着下一个事务将看到上一个事务的结果,就好像在一个事务中进行的一样。
-
在提交子事务时就可以释放不需要的数据对象,而不必等到整个事务完成后才释放。
链事务工作示意图:
链事务与扁平事务的区别:
-
链事务中的回滚仅限于当前事务,相当于只能恢复到最近的一个保存节点
-
带保存节点的扁平事务能回滚到任意正确的保存点
-
带有保存节点的扁平事务中的保存点是易失的,当发生系统崩溃是,所有的保存点都将消失,这意味着当进行恢复时,事务需要从开始处重新执行。
3.3、嵌套事务
什么是嵌套事务?
-
嵌套事务(Nested Transaction)是一个层次结构框架。
-
由一个顶层事务(top-level transaction)控制着各个层次的事务。
-
顶层事务之下嵌套的事务成为子事务(subtransaction),其控制着每一个局部的操作,子事务本身也可以是嵌套事务.
-
嵌套事务的层次结构可以看成是一颗树.
嵌套事务结构如下图所示:
事务的隔离级别
SQL标准定义的四个隔离级别:
- READ UNCOMMITTED: 未提交读,作用域为(global)全局 、(session)当前会话
mysql> set @@global.tx_isolation='READ-UNCOMMITTED';
Query OK, 0 rows affected (0.00 sec)
mysql> set @@session.tx_isolation='READ-UNCOMMITTED';
Query OK, 0 rows affected (0.00 sec)
- READ COMMITTED: 提交读
mysql> set @@global.tx_isolation='READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)
- REPEATABLE READ: 可重复读
mysql> set @@global.tx_isolation='REPEATABLE-READ';
Query OK, 0 rows affected (0.00 sec)
- SERIALIZABLE: 可串行读
mysql> set @@global.tx_isolation='SERIALIZABLE';
Query OK, 0 rows affected (0.00 sec)
4.1、Read uncommitted
读取未提交内容。在该隔离级别下,所有事务都可以看到其它未提交事务的执行结果。
-
事务2查询到的数据是事务1中修改但未提交的数据
-
但因为事务1回滚了数据,所以事务2查询的数据是不正确的
-
因此出现了脏读的问题
4.2、Read committed
-
读取提交内容。在该隔离级别下,一个事务从开始到提交之前对数据所做的改变对其它事务是不可见的,这样就解决在READ-UNCOMMITTED级别下的脏读问题。
-
但如果一个事务在执行过程中,其它事务的提交对该事物中的数据发生改变,那么该事务中的一个查询语句在两次执行过程中会返回不一样的结果
-
事务2执行update语句但未提交前,事务1的前两个select操作返回结果是相同的。
-
但事务2执行commit操作后,事务1的第三个select操作就读取到事务2对数据的改变,导致与前两次select操作返回不同的数据
-
因此出现了不可重复读的问题。
4.3、Repeatable read
-
可重复读。这是MySQL的默认事务隔离级别,能确保事务在并发读取数据时会看到同样的数据行,解决了READ-COMMITTED隔离级别下的不可重复读问题
-
MySQL的InnoDB存储引擎通过多版本并发控制(Multi_Version Concurrency Control, MVCC)机制来解决该问题
-
在该机制下,事务每开启一个实例,都会分配一个版本号给它,如果读取的数据行正在被其它事务执行DELETE或UPDATE操作(即该行上有排他锁),这时该事物的读取操作不会等待行上的锁释放,而是根据版本号去读取行的快照数据(记录在undo log中)
-
这样,事务中的查询操作返回的都是同一版本下的数据,解决了不可重复读问题
-
虽然该隔离级别下解决了不可重复读问题,但理论上会导致另一个问题:幻读(Phantom Read)
-
正如上面所讲,一个事务在执行过程中,另一个事物对已有数据行的更改
-
MVCC机制可保障该事物读取到的原有数据行的内容相同,但并不能阻止另一个事务插入新的数据行,这就会导致该事物中凭空多出数据行,像出现了幻读一样,这便是幻读问题
-
事务2对id=1的行内容进行了修改并且执行了commit操作
-
事务1中的第二个select操作在MVCC机制的作用下返回的仍是v=1的数据
-
事务3执行了insert操作
-
事务1第三次执行select操作时便返回了id=2的数据行,与前两次的select操作返回的值不一样
需要说明的是,REPEATABLE-READ隔离级别下的幻读问题是SQL标准定义下理论上会导致的问题,MySQL的InnoDB存储引擎在该隔离级别下,采用了Next-Key Locking锁机制避免了幻读问题。Next-Key Locking锁机制将在后面的锁章节中讲到。
4.4、Serializable
-
可串行化。这是事务的最高隔离级别
-
通过强制事务排序,使之不可能相互冲突,就是在每个读的数据行加上共享锁来实现
-
在该隔离级别下,可以解决前面出现的脏读、不可重复读和幻读问题
-
但也会导致大量的超时和锁竞争现象,一般不推荐使用