MySQL是如何实现事务的?
事务日志
redo log(重做日志)
redo log 是物理日志,记录了某个数据页做了什么修改,比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新,每当执行一个事务就会产生这样的一条或者多条物理日志。
在事务提交时,只要先将 redo log 持久化到磁盘即可,可以不需要等到将缓存在 Buffer Pool 里的脏页数据持久化到磁盘。
当系统崩溃时,虽然脏页数据没有持久化,但是 redo log 已经持久化,接着 MySQL 重启后,可以根据 redo log 的内容,将所有数据恢复到最新的状态。它保证了事务的 ACID 特性中的持久性(Durability)和隔离性(Isolation)。
undo log(回滚日志)
如果我们每次在事务执行过程中,都记录下回滚时需要的信息到一个日志里,那么在事务执行中途发生了 MySQL 崩溃后,就不用担心无法回滚到事务之前的数据,我们可以通过这个日志回滚到事务之前的数据。
实现这一机制就是 undo log(回滚日志),它保证了事务的 ACID 特性中的原子性(Atomicity)。
锁机制
利用行锁、间隙锁等等,控制数据的并发修改。它保证了事务的 ACID 特性中的隔离性(Isolation)。
MVCC(Multi-Version Concurrency Control 多版本并发控制)
一种并发控制机制,允许多个事务同时读取和写入数据。它保证了事务的 ACID 特性中的隔离性(Isolation)。
在mvcc中数据库为每一个事务创建一个数据快照。每当数据被修改时,MySQL不会立刻覆盖原有的数据,而是生成新版本的记录。每个记录保存对应的版本号或时间戳。
多版本之间串联起来就形成了一条版本链,这样不同的时刻启动的事务可以无锁地获取不同版本的数据。
实际上mvcc的多版本不是存储了多个版本的过程,而是借助undo log记录每次操作的反向操作,根据记录的反向操作得到数据的历史版本。
MySQL事务的二阶段提交
为了保证redo log和bin log一致的一种机制。通过二阶段提交机制,保证在崩溃恢复时,不会出现数据不一致或者丢失的情况。
二阶段的两个阶段
- 准备阶段:事务提交时,先写入redo log,并标记为prepare,表示事务已提交但是并未完成。
- 提交阶段:当redo log标记为prepare,开始写入bin log,写入成功后会通知InnoDB,修改redo log状态为commit,整个事务提交完成。
为什么需要二阶段提交
如果没有二阶段
- 先写redo log,再写bin log
- 写完redo log后,MySQL宕机重启,redo log恢复事务的修改,但是bin log没有本次事务的提交数据,bin log恢复时就没有本次提交的数据,数据和宕机前不一致了。
- 先写bin log,再写redo log
- 写完bin log后,MySQL宕机重启,redo log没有记录操作,redo log恢复时就没有恢复这次操作,但是bin log记录了,bin log恢复就有本次事务的修改数据,数据和宕机前不一致了
如果有二阶段
- redo log处于prepare状态,bin log还未写入时
- redo log还在准备阶段,MySQL宕机重启,redo log记录的数据不作数,bin log也没数据,所有数据和宕机前一致。
- redo log处于prepare状态,bin log已写入,但是redo log还未变成commit状态
- 对比redo log和bin log的数据是否一致,一致则提交事务,恢复数据,不一致则回滚事务。
如何对比redo log和bin log的数据是否一致
两个日志都有一个字段XID,MySQL宕机重启,用prepare状态的redo log里的XID去bin log查询,如果有数据,则提交事务恢复数据,没有则回滚事务。
组提交
用于优化redo log的写入过程。它将多个事务的redo log刷盘操作合并成一次磁盘同步,从而减少fsync的调用次数。
