小白也能看懂的Innodb执行引擎及MVCC原理
一、InnoDB实现事务四大特性
- 原子性(Atomicity):原子性指整个数据库事务是不可分割的工作单位。事务中所有的数据库操作,要么全部提交成功,要么全部失败回滚。
- 一致性(Consistency):数据库总是从一个一致性的状态转换到另一个一致性的状态。
- 隔离性(Isolation):事务的隔离性要求每个读写事务的对象对其他事务的操作对象能相互分离,即该事务提交前对其他事务都不可见。
- 持久性(Durability):事务一旦提交,其结果是永久性的,即使发生宕机等故障,数据库也能将数据恢复。
二、InnoDB的实现原理
(一)InnoDB如何保证原子性(Atomicity)?
在事务里任何对数据的修改都会写一个undo log,然后进行数据的修改,如果出现错误,存储引擎会利用undo log的备份数据恢复到事务开始之前的状态。
(二)InnoDB如何保证一致性(Consistency)?
事务的原子性和隔离性保证了数据的一致性
(三)InnoDB如何保证隔离性(Isolation)?
InnoDB的默认隔离级别REPEATABLE READ + Next-Key Locking,InnoDB锁与MVCC机制保证了数据库的隔离性,不出现并发一致性问题,且理论上效率高于SERIALIZABLE隔离级别。
(四)InnoDB如何保证持久性(Durability)?
InnoDB存储引擎在启动时不管上次数据库运行时是否正常关闭,都会尝试通过redo log进行恢复操作。
三、并发一致性问题
- 脏读:读到未提交的数据
- 不可重复读:读到已提交的数据;同样的条件,第一次与第二次读的值不同。
- 幻读:同样的条件,第一次与第二次读出来的记录不同。
- 丢失修改 : 事务2提交的结果破坏了事务1提交的结果,导致事务1进行的修改丢失。
四、隔离级别
- READ UNCOMMITTED(读未提交):事务中的修改,即使没有提交,对其它事务也是可见的。任何操作都不加锁。
- READ COMMITTED(读已提交):一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。数据的读取不加锁,但数据的写入、修改和删除都加锁。
- REPEATABLE READ(可重复读):保证在同一个事务中多次读取同样数据的结果是一样的。MVCC机制让数据变得可重复读。
- SERIALIZABLE(可串行化):强制事务串行执行。全部操作加悲观锁。读加共享锁,写加排他锁。
五、Undo log
Undo意为取消,以撤销操作为目的,返回指定某个状态的操作。
(1)Undo log指事务开始之前,在操作任何数据之前,首先将需操作的数据备份到一个地方 (Undo Log)。
(2)Undo Log实现了事务的原子性:事务处理过程中如果出现了错误或者用户执行了 ROLLBACK语句,MySQL可以利用Undo Log中的备份将数据恢复到事务开始之前的状态。
(3)MySQL InnoDB通过Undo log实现MVCC:事务未提交之前,Undo log保存了未提交之前的版本数据,Undo log中的数据可作为数据旧版本快照供其他并发事务进行快照读。
-
快照读
SQL读取的数据是快照版本,也就是历史版本,普通的SELECT就是快照读。 -
当前读
SQL读取的数据是最新版本。通过锁机制来保证读取的数据无法通过其他事务进行修改。
UPDATE、DELETE、INSERT、SELECT … LOCK IN SHARE MODE、SELECT … FOR UPDATE都是当前读。
假设有A、B两个数据,值分别为1,2。
- 事务开始.
- 记录A=1到undolog.
- 修改A=3.
- 记录B=2到undolog.
- 修改B=4.
- 将undolog写到磁盘。
- 将数据写到磁盘。
- 事务提交
这里有一个隐含的前提条件:数据都是先读到内存中,然后修改内存中的数据,最后将数据写回磁盘。
之所以能同时保证原子性和持久化,是因为以下特点:
- 更新数据前记录Undo log。
- 为了保证持久性,必须将数据在事务提交前写到磁盘。只要事务成功提交,数据必然已经持久化。
- Undo log必须先于数据持久化到磁盘。如果在G,H之间系统崩溃,undo log是完整的,可以用来回滚事务。
- 如果在1-8之间系统崩溃,因为数据没有持久化到磁盘。所以磁盘上的数据还是保持在事务开始前的状态。
六、MVCC
Multi Version Concurrency Control (多版本并发控制),并发访问(读或写)数据库时,对事务内正在处理的数据做多版本管理。以实现写操作堵塞的同时,依然可以进行读操作。
InnoDB为每行记录都实现了两个重要隐藏字段,用来实现MVCC:
- 6字节的事务ID(DB_TRX_ID) ,每处理一个事务,其值自动+1。
- 7字节的回滚指针(DB_ROLL_PTR),指向写到rollback segment(回滚段)的一条undo log记录。
- 隐藏的ID
MVCC-插入
插入规则:在数据行版本号列保存当前事务ID。
MVCC-删除
删除规则:将删除版本号列更新为当前事务ID。
MVCC-修改
修改规则:现将命中的数据行copy,将原数据的删除版本号设置为当前事务ID(33)。
MVCC-查询
查询规则:
-
查找数据行版本小于或等于当前事务版本的数据行。这样可以确保事务读取的行,要么是在事务开始之前就存在的,要么是事务自身插入或修改过的;
-
查找删除版本号要么为NULL,要么大于当前事务版本号的数据行。这样可以确保查询出来的记录在事务开启前没有被删除。
缺陷:
1. 每个事务提交前将数据和Undo Log写入磁盘,这样会导致大量的磁盘IO,因此性能很低。
2. 如果能够将数据缓存一段时间,就能减少IO提高性能。但是这样就会丧失事务的持久性。因此引入了另外一种机制来实现持久化,即Redo log
七、Redo log
Redo意为重做。以恢复操作为目的,重现操作。Redo log指事务中操作的任何数据,将最新的数据备份到一个地方 (Redo Log)。
Redo log实现了事务的持久性:
Redo log一般是在事务提交的时候以顺序IO的方式写入磁盘。具体的落盘策略可以进行配置。防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据Redo log进行重做,从而保证事务中尚未写入磁盘的数据的持久性。
重做日志由两部分组成:内存中重做日志缓冲和重做日志文件。
当事务提交时,必须先将该事务的所有日志写入到重做日志进行持久化。在每次将重做日志缓冲写入重做日志文件后,InnoDB存储引擎都需要调用一次fsync操作,来确保日志写入磁盘。由于fsync操作取决于磁盘,因此磁盘性能决定了事务提交的性能。
Redo log记录的是新数据的备份。在事务提交前,只要将Redo Log持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,
但是RedoLog已经持久化。系统可以根据RedoLog的内容,将所有数据恢复到最新的状态。
事务的简化过程
假设有A、B两个数据,值分别为1,2.
A.事务开始.
B.记录A=1到undolog.
C.修改A=3.
D.记录A=3到redolog.
E.记录B=2到undolog.
F.修改B=4.
G.记录B=4到redolog.
H.将redolog写入磁盘。
I.事务提交
参考:
[1] InnoDB如何保证事务四大特性
[2] MySQL MVCC Undo log Redo log