面向面试编程:MySQL的Redo Log
面试官:事务提交后,MySQL在buffer pool中的数据会不会丢失?
在MySQL中提交事务,更新完Buffer Pool中的缓存页之后,必须要写一条redo log,这样才能记录下来我们对数据库做的修改。
redo log可以保证我们事务提交之后,如果事务中的增删改SQL语句更新的缓存页还没刷到磁盘上去,此时MySQL宕机了,那么MySQL重启过后,就可以把redo log重做一遍,恢复出来事务当时更新的缓存页,然后再把缓存页刷到磁盘就可以了。
redo log本质是保证事务提交之后,修改的数据绝对不会丢失的。
redo日志如何保证事务提交后数据绝对不会丢失
执行增删改SQL语句的时候,都是针对一个表中的某些数据去执行的,首先必须找到这个表对应的表空间,然后找到表空间对应的磁盘文件,接着从磁盘文件里把你要更新的那批数据所在的数据页从磁盘读取出来,放到Buffer Pool的缓存页里去。接着实际上你的增删改SQL语句就会针对Buffer Pool中的缓存页去执行你的更新逻辑,比如插入一行数据,或者更新一行数据,或者是删除一行数据。
其实你更新缓存页的时候,会更新free链表、flush链表、lru链表,然后有专门的后台IO线程,不定时的根据flush链表、lru链表,会把你更新过的缓存页刷新回磁盘文件的数据页里去。所以大家都知道这个机制里最大的漏洞就在于,万一你一个事务里有增删改SQL更新了缓存页,然后事务提交了,结果万一你还没来得及让IO线程把缓存页刷新到磁盘文件里,此时MySQL宕机了,然后内存数据丢失,你事务更新的数据就丢失了!
但是也不可能每次你事务一提交,就把你事务更新的缓存页都刷新回磁盘文件里去,因为大家之前也都知道,缓存页刷新到磁盘文件里,是随机磁盘读写,性能是相当的差!这会导致你数据库性能和并发能力都很弱的!所以此时才会引入一个redo log机制,这个机制就是说,你提交事务的时候,绝对是保证把你对缓存页做的修改以日志的形式,写入到redo log日志文件里去的。这种日志大致的格式如下:对表空间XX中的数据页XX中的偏移量为XXXX的地方更新了数据XXX。
只要你事务提交的时候保证你做的修改以日志形式写入redo log日志,那么哪怕你此时突然宕机了,也没关系!因为你MySQL重启之后,把你之前事务更新过做的修改根据redo log在Buffer Pool里重做一遍就可以了,就可以恢复出来当时你事务对缓存页做的修改,然后找时机再把缓存页刷入磁盘文件里去。
一行redo log可能就占据几十个字节,就包含表空间号、数据页号、磁盘文件偏移量、更新值,这个写入磁盘速度很快。此外,redo log写日志,是顺序写入磁盘文件,每次都是追加到磁盘文件末尾去,速度也是很快的。所以你提交事务的时候,用redo log的形式记录下来你做的修改,性能会远远超过刷缓存页的方式,这也可以让你的数据库的并发能力更强。
redo log是如何写入文件的
MySQL内有另外一个数据结构,叫做redo log block,大概你可以理解为,平时我们的数据是存放在数据页,用一页一页的数据页来存放数据。那么对于redo log也不是单行单行的写入日志文件的,他是用一个redo log block来存放多个单行日志的。一个redo log block是512字节,这个redo log block的512字节分为3个部分,一个是12字节的header块头,一个是496字节的body块体,一个是4字节的trailer块尾。
在这里面,12字节的header头又分为了4个部分。
包括4个字节的block no,就是块唯一编号;2个字节的data length,就是block里写入了多少字节数据;2个字节的first record group。这个是说每个事务都会有多个redo log,是一个redo log group,即一组redo log。那么在这个block里的第一组redo log的偏移量,就是这2个字节存储的;4个字节的checkpoint on。
所以其实对于我们的redo log而言,他确实是不停的追加写入到redo log磁盘文件里去的,但是其实每一个redo log都是写入到文件里的一个redo log block里去的,一个block最多放496字节的redo log日志。
此时可能有人会有疑问了,到底一个一个的redo log block在日志文件里是怎么存在的?那么一条一条的redo log又是如何写入日志文件里的redo log block里去的呢?假设你有一个redo log日志文件,平时我们往里面写数据,你大致可以认为是从第一行开始,从左往右写,可能会有很多行,那么所以现在既然如此,假设你要写第一个redo log了,是不是应该起码是先在内存里把这个redo log给弄到一个redo log block数据结构里去?然后似乎你应该是等内存里的一个redo log block的512字节都满了,再一次性把这个redo log block写入磁盘文件?然后其实按照我们所说的,一个redo log block就是512字节,那么是不是真正写入的时候,把这个redo log block的512字节的数据,就写入到redo log文件里去就可以了?那么redo log文件里就多了一个block。
redo log buffer中的缓冲日志何时写入磁盘
- 如果写入redo log buffer的日志已经占据了redo log buffer总容量的一半了,也就是超过了8MB的redo log在缓冲里了,此时就会把它们刷入到磁盘文件里去。
- 一个事务提交的时候,必须把他的那些redo log所在的redo log block都刷入到磁盘文件里去,只有这样,当事务提交之后,他修改的数据绝对不会丢失,因为redo log里有重做日志,随时可以恢复事务做的修改。
- 后台线程定时刷新,有一个后台线程每隔1秒就会把redo log buffer里的redo log block刷到磁盘文件里去。
- MySQL关闭的时候,redo log block都会刷入到磁盘里去。
转载自:https://juejin.cn/post/7150621874232229901