引言
日志日志,在我们平时开发中主要的用途在于监控、备份,但在MySQL中,日志的功能远远不止这些,分别有用于记录的慢查询日志,回滚版本的undolog,宕机恢复的redolog、全量备份的binlog等等,而这些日志,也刚好是我们事务的原理
🎏本篇速览脑图
🎯undolog – 原子性
回滚日志,记录数据被修改前的信息,属于逻辑日志
- 什么是逻辑日志?
比如我们执行一条delete语句,undolog里边记录的是相反的操作insert记录【相当于存放的是操作逻辑语句,而不是数据】
🤷♂️逻辑日志好处
- 比如全表更新,如果是物理日志,我们需要把全表的数据都存下来
- 若是逻辑日志,只需要存放一条语句就可以恢复了
undolog用处
回滚
一个事务在执行过程中,在还没有提交事务之前,如果MySQL 发生了崩溃,要怎么回滚到事务之前的数据呢?
- 在事务没提交之前,MySQL 会先记录更新前的数据到 undo log 日志文件里面,当事务回滚时,可以利用 undo log 来进行回滚。
版本链
类似一个链表,通过回滚指针,串联起来
一条记录的每一次更新操作产生的 undo log 格式都有一个 roll_pointer 指针和一个 trx_id 事务id:
- 通过 trx_id 可以知道该记录是被哪个事务修改的;
- 通过 roll_pointer 指针可以将这些 undo log 串成一个链表,这个链表就被称为版本链;
MVCC
通过 ReadView + undo log 实现 MVCC
🎯redolog – 持久性
重做日志,物理记录
redo log 是物理日志,记录内容是“在某个数据页上做了什么修改”,属于 InnoDB 存储引擎。
前置知识-- Buffer Pool
Buffer Pool是很经典的缓存池,其中又以Page为单位
- 空闲页
- 干净页
- 脏页【类似操作系统中,修改位为1】
正常同步
首先我们要知道,每次事务提交后,首先是跟 Buffer pool 打交道
- 若缓存中没有我们要操作的数据【类似缺页中断,MySQL 中数据是以页为单位】,则会启动后台线程,去磁盘中拉取过来
- 之后就都是直接操作缓存了
- 现在缓存的内容更新好了,但磁盘的内容还是旧的,何时更新到磁盘呢?
我们可能会有如下方案:
- 每隔一段时间,就同步到磁盘
弊端
每次更新都要写入磁盘,磁盘IO很高
如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很高。 不如先把更新的操作,记录在redolog日志里边,之后在适当的时候,再一起刷盘!【而不是每次更新就一条一条的刷】
这就是MySQL的 WAL 技术,(Write-Ahead Logging) 先写进日志,再刷入磁盘
buffer挂掉
一般情况下都没什么问题,但可能在执行某次更新操作的时候,buffer挂掉了呢?此时同步过程就GG了,导致缓存不一致
- 因此,为了双保险,我们还会把数据相关的变化【物理修改】存到redolog里边,用来实现事务的持久性【宕机后还能查询redolog,来进行恢复】
redolog保险出场
当事务提交时,会把“在某个数据页上做了什么修改”记录到重做日志缓存(redo log buffer)里,接着刷盘到 redo log 文件里。