MySQL:为什么要引入redo log机制

什么是redo log

  • 在我们执行CRUD操作时,首先会在buffer pool中更新缓存页,更新完buffer pool中的缓存页之后,必须要写一条redo log,这样才能记录下来我们对数据库做的修改
  • redo log可以保证我们事务提交之后,如果事务中的增删改SQL语句更新的缓存页还没有刷到磁盘上去,此时MySQL宕机了,就可以把redo log重做一遍,恢复出来事务当前更新的缓存页,然后再把缓存页刷到磁盘就可以了
  • redo log本质是保证事务提交之后,修改的数据绝对不会丢失
    在这里插入图片描述

redo 机制存在的意义

  • 执行增删改SQL语句的时候,都是针对一个表的某些数据去执行的,此时首先必须找到这个表对应的表空间,然后找到表空间对应的磁盘文件,接着从磁盘文件里把你要更新的那批数据所在的数据页从磁盘读取出来,放到buffer pool的缓存页里去。

在这里插入图片描述

  • 接着增删改SQL语句就会针对buffer pool中的缓存页去执行更新逻辑

在这里插入图片描述

  • 更新缓存的时候,会更新free链表、fflush链表、lru链表,然后后台有专门的IO线程,不定时的根据free链表、fflush链表、lru链表,把你更新后的缓存页刷新会磁盘文件的数据页里面去
    在这里插入图片描述

  • 但是这个机制有一个很大的漏洞:万一一个事务里有增删改SQL更新了缓存页,然后事务提交了,结果万一还没来得及让IO线程把缓存页刷新到磁盘,此时MySQL宕机了,然后内存数据丢失,那事务更新的数据也会丢失。

  • 但是也不可能每次事务一提交,就把事务更新的缓存页都刷新会磁盘文件里去。因为缓存页刷新到磁盘文件里,是随机磁盘读写,性能会很差,这会导致数据库性能和并发能力都很弱

  • 所以此时才会引入一个redo log机制:提交事务的时候。保证把你对缓存页做的修改以日志的形式,写入到redo log日志文件里去的

  • 这种日志大致的格式如下:对表空间XX中的数据页XX中的偏移量为XXXX的地方更新了数据XXX

在这里插入图片描述

  • 只要事务提交的时候保证了你做的修改以日志形式写入redo log日志,那么此时哪怕机器突然宕机,也没有关系。因为MySQL重启之后,会把之前事务更新过做的修改根据redo log在buffer pool里重做一遍就可以了,就可以恢复出来当时你事务对缓存页做的修改,然后找时机再把缓存页刷到磁盘文件里去,

问题:事务提交的时候把修改过的缓存页刷入磁盘,跟事务提交的时候把做的修改的redo log写入日志文件,它们不都是写磁盘吗?差别在哪里?

实际上,把修改过的缓存页都刷入磁盘:

  • 缓存页是一个16KB,数据比较大,刷入磁盘比较耗时,而且可能就修改了缓存页里的几个字节的数据,难道也需要把完整的缓存页刷入磁盘吗?
  • 而且缓存页刷入磁盘是随机写磁盘,性能很差。

redo log刷入磁盘:

  • 第一个一行redo log可能就占据几十个字节,这个写入磁盘速度很快
  • 而且写日志是一个顺序写磁盘,每次都是追加到磁盘文件末尾去,速度也是很快的。

所以提交事务的时候,用redo log的形式记录下你做的修改,性能就会远远超过刷缓存页的方式,这也可以让数据库的并发能力更强。

redo log到底长什么样

redo log本质上记录的是在对某个表空间的某个数据页的某个偏移量的地方修改了几个字节的只,具体修改的值是什么,它里面记录的就是表空间号+数据页号+偏移量+修改几个字节的值+具体的值

所以根据你修改了数据页里的几个字节的值,redo log就划分为不同的类型:

  • MLOG_1BYTE类型的日志指的是修改了1个字节的值
  • MLOG_2BYTE类型的日志指的是修改了2个字节的值
  • 以此类推,还有修改了4个字节的值的日志类型,修改了8个字节的值的日志类型。

当然,如果要是修改了一大串的值,类型就是MLOG_WRITE_STRING,表示一下子在那个数据页的某个偏移量的位置插入会在修改了一大串的值。

所以一条redo log看起来的结构如下:

日志类型(就是类似MLOG_1BYTE之类的),表空间ID,数据页号,数据页中的偏移量,具体修改的数据
  • 类型:告知这次增删改操作修改了多少字节的数据
  • 表空间ID:告知在那个表空间里操作的,与SQL在那个表里执行对应
  • 数据页号:在这个表空间的哪个数据页里执行的
  • 数据页中的偏移量:在数据页的哪个偏移量开始执行的
  • 具体修改的数据:具体更新的数据时哪些

有了上述信息,就可以精准完美的还原出来一次数据增删改操作做的变动了。

只不过如果是MLOG_WRITE_STRING类型的日志,因为不知道具体修改了多少字节的数据,所以其实会多一个修改数据长度,就告诉你他这次修改了多少字节的数据,如下所示他的格式:

日志类型(就是类似MLOG_1BYTE之类的),表空间ID,数据页号,数据页中的偏移量,修改数据长度,具体修改的数据

redo log是直接一条一条的写入文件的吗?

平时我们执行CRUD的时候,从磁盘加载数据页到buffer pool的缓存页里去,然后对缓存页执行增删改,同时还会写redo log到日志文件里去,后继不定时把缓存页刷回磁盘文件里去
在这里插入图片描述

redo log的样子:表空间号+数据页号+数据页内偏移量+修改了几个字节的数据+实际修改数据

那么,redo log就是按照上述格式,一条一条的直接写入到磁盘的日志文件里去的吗?

当然不是

redo log block

MySQL内有另外一个数据结构,叫做redo log block。这个数据结构可以存放多个单行redo log日志。

一个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 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 buffer满了),再一次性把这个redo log block写入磁盘文件

在这里插入图片描述

  • 一个redo log block就是512字节,真正写入的时候,把这个redo log block的512字节的数据,写入到redo log文件里去就可以了。此后redo log文件里就多了一个block。

在这里插入图片描述

  • 写文件的时候,可以按照字节,一个字节一个字节的写入,文件里存放的东西就是很多很多字节,一次排开,然后其中可能512个字节组合起来,就固定代表了一个redo log block。

这其实就是任何一个中间件系统、数据库系统、底层依赖磁盘文件存储数据的一个共同的原理。

  • 如果依次在磁盘文件的末尾不停的追加写字节数据,就是磁盘顺序写;
  • 假设现在磁盘文件里已经有很多很多的redo log block了,此时要在磁盘里某个随机位置找到一个redo log block去修改它里面几个字节的数据,这就是磁盘随机写。

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值