InnoDB是如何实现事务的四大特性的

事务的四大特性

事务的四大特性是原子性,一致性,隔离性,持久性。先简单的描述下这四个特性:

  1. 原子性Atomicity:一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
  2. 一致性Consistency:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。
  3. 隔离性Isolation:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括未提交读(Read uncommitted)、提交读(read committed)、可重复读(repeatable read)和串行化(Serializable)。
  4. 持久性Durability:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

InnoDB引擎支持了事务,那么他是如何实现事务的呢,下面让我们逐一说明一下:

原子性实现

使用Undo log实现

数据库的每一条语句都会记录对应的Undo log,并且提前持久化到磁盘,如果事务选择回滚,那么会根据Undo log进行数据的逆转;
逆转的操作根据sql语句的不同会有不同的提现,如:

  1. insert语句在回滚的时候逆转为delete
  2. delete语句在回滚的时候逆转为insert
  3. update语句在回滚的时候修改为原值

隔离性实现

重点!不要忽略:这里的加锁方式是MySQL官网8.0版本官方文档说明的,建议大家根据自己使用的版本实际查询一下对应版本的文档

使用mvcc+读写锁实现

InnoDB支持四个隔离级别,针对不同的隔离级别有不同的实现:

READ UNCOMMITED (读未提交)

第一种情况:SELECT语句以非锁定的方式执行,但是早期版本可能使用。

第二种情况:其他情况下同READ COMMITTED。

READ COMMITED (提交读)

第一种情况:SELECT同一事物中的一致读设置并去读新的快照。

第二种情况:SELECT带有FOR UPDATE或FOR SHARE、UPDATE或DELETE,使用记录锁(Record Locks)。

对锁处理的额外细节:

  1. 对于UPDATE或DELETE语句,只会持有需要修改的行的记录锁(Record Locks),MySQL进行评估where条件后,释放掉不匹配的记录锁(Record Locks),
    这保证了部分死锁情况发生,但是不能保证死锁不会发生。
  2. 对于UPDATE语句,如果某一行已经锁定,那么会采用”半一致”策略,将最新的提交版本返回给MySQL,MySQL再评估是否符合WHERE条件,如果匹配WHERE条件,
    则InnoDB则需要加记录锁(Record Locks),并在加锁的时候堵塞。
REPEATABLE READ (可重复读)

第一种情况:SELECT同一事物中的一致读将读取当前事务创建时的快照。

第二种情况:SELECT带有FOR UPDATE或FOR SHARE、UPDATE或DELETE,取决于语句使用的是唯一搜索条件的唯一索引还是范围索引;

如果使用唯一搜索条件的唯一索引,则使用记录锁(Record Locks)。

如果使用的范围索引,则使用间隙锁(gap locks) 或(next-key locks)。

SERIALIZABLE (串行化)

类似于REPEATABLE READ级别,对SQL进行了隐式转换:
第一种转换:SELECT语句默认转换成了SELECT … FOR SHARE
第二种转换:autocommit禁用

持久性实现

使用Redo log实现

重做日志(Redo Log)在修改数据之后,事务提交之前发生,保证了事务提交的时候重做日志(Redo Log)必然存在,
而没有进行提交的事务是不会恢复的,在数据库发生崩溃重启的时候,会根据Redo Log纠正事务不完整写入的数据

一致性实现

原子性+隔离性+持久性共同保障一致性