深探redo log,原来如此简单
一、回顾
对于redo log在之前的文章将Buffer Pool的时候简单介绍过它的作用。这里我们再来回顾一下:
平时我们在CRUD代码的时候,会不断地对数据库进行操作,数据库会先将数据从磁盘上加载到Buffer Pool的free链表中一个空的缓存页上,之后就基于内存对其进行操作,如果更新了数据页中的某些数据,那么这条数据就会被加如到flush链表和LRU链表中,之后等待后台的IO线程根据LRU链表将脏数据页刷入磁中。
-
我们首先来简单想一下,首先不考虑各种日志,上述的过程可能会出现什么问呢?
首先第一点很明显,因为数据是基于内存中进行CRUD的,所以数据很可能在还没来得及刷入磁盘之前就会因为宕机等情况而丢失,这个问题对于数据库来说是致命的!
-
redo log为什么会出现?
redo log其实就是为解决上述问题而出现的,它所作的其实说白了,就是为了持久化数据,防止其丢失。其作用就类似于Redis中的AOF或RDB文件,殊途同归。
-
redo log是怎么做到的?
看过之前Buffer Pool文章的应该都知道,我们在对数据进行增删改操作的时候,其实都会在redo log里面对所作的操作进行一个记录。这样做的好处就是,万一突然发生宕机,其实也不用担心会丢失,因为MySQL在重启之后将redo log中记录的操作加载到内存中重做一遍。举个例子:你刚执行完一个SQL——update ‘xxx’ set colum1 = ‘xxx’ where colum2 = ‘xxx’,之后redo log便会记录你的这次操作,此时你杠修改完内存中的数据,还没来得及等数据被刷入磁盘MySQL就宕机了,然后MySQL重启后将redo log中的内容加载到内存中重做一遍之前的操作,这样就保证了数据不丢失。
再补充一点:因为写redo log是顺序写,因此速度很快,因此不用太担心因为写redo log日志所带来的性能下降问题
二、redo log中到底记录了什么内容?
在揭晓这个答案之前,我们先回顾一下redo log的作用——在MySQL崩溃重启后用于数据恢复,恢复的方法就是将系统崩溃之前的操作记录下来,那你也许会想,redo log记录的是SQL语句吗?其实,你这样想也没错,因为这样做其实也可以,只不过会有一个问题,慢!为什么呢?因为SQL仅仅只是一种查询语言,对于我们程序猿来说是很好理解的,但是对于数据库这不是,他如果想要读懂你写的SQL,必须进行SQL语句的解析才能理解你的意思。因为我们经常所说的表,列,行这些东西对于InnoDB引擎来说他都是不知道的,它只知道表空间,数据页这样的概念。说白了就是逻辑层面和物理层面的区别。
-
先说说我们一条SQL语句的基本要素:
- 操作哪个表
- 操作表中哪一条记录
- 操作后的值
-
根据上面的内容,我们来对比看看redo log中一条记录的格式:
日志类型 表空间ID 数据页号 页内偏移量 修改的字节数 修改后的值
我们对比着看的时候,就会发现他们还是有很多共同点的。
redo log中有一个日志类型,目的就是标志这条日志修改了多少字节的值,比如:
MLOG_1BYTE: 修改1bit的值 MLOG_2BYTE: 修改2bit的值 MLOG_4BYTE: 修改4bit的值 MLOG_8BYTE: 修改8bit的值 MLOG_WRITE_STRING(具体修改多少字节就需要看后面的修改的字节数了)
-
这种记录格式的优点是什么?
- 数据恢复速度快。如果是SQL语句可能要先扫描,找到数据页,然后才进行操作,但是如果是根据redo log来重做的话就可以直接找到表空间,然后再找到对应的数据页,再根据偏移量找到需要修改的记录。
- 占用空间小。由于内容精简,只保存了用于数据恢复所需要的信息
三、redo log日志是怎么写到磁盘上的?
我们现在知道的是,当我们每执行一个增删改操作都会在redo log上写上一条日志,因为redo log是在磁盘上的,难道说每次执行增删改SQL的时候都会进行I/O操作吗?问到这里,想必你已经猜到了肯定不会这样做,在之前的文字中也讲过,MySQL每次进行I/O时一般最少都是16k的数据,如果每次都为了这不到1k的redo log就进行16k的磁盘I/O无疑会很浪费。
为了解决上面的问题,MySQL的做法是利用log buffer来解决
-
log buffer是什么
上面的图片很清晰,在redo log日志写入到磁盘上之前,首先会保存在log buffer中,log buffer的大小默认为16MB,由
innodb_log_buffer_size
变量定义,并且log buffer中的内容会被定时的刷到磁盘上去。刷入磁盘的策略由
innodb_flush_log_at_trx_commit
变量控制- 1(默认):日志在每次事务提交时写入并刷新到磁盘,符合ACID性质
- 0:每秒将日志写入并刷新到磁盘一次,未刷新日志的事务可能会在崩溃后丢失
- 2:日志在每次事务提交后写入,并每秒刷新到磁盘一次。未刷新日志的事务可能会在系统崩溃后丢失
现在我们思考一下仅仅是这样是否就解决了我们上面的问题了呢?
其实并没有完全解决,现在只解决了每次执行增删改都要往磁盘上的redo log写日志的问题,日志可以暂存在log buffer中,这个只解决了部分问题,那刷磁盘的时候具体应该怎么做呢?到底每次刷多少数据到磁盘上呢?如果有事务的情况下呢?面对这些问题是不是发现单靠log buffer就难以招架了。别慌,还有另一位神秘人物
-
redo log block
它是在log buffer的基础上更细粒度的划分,每个redo log block大小为512字节,其中496字节是body体,用于保存日志数据,其余的字节用于记录头信息和尾信息,长下面这样:
header中主要记录了如下内容:
- 块编号(标记是哪一个block)
- data length(记录以及写了多少字节的数据了)
- first record group(为了保证事务提交后,同一个事务的redo log都会被一起刷入到磁盘上,一个事物内多个redo log都被称为redo log group)
- checkpoint on
-
流程
我们不断的写redo log日志,其实会先将它们不断地写到一个redo log block中,等这个redo log block满了之后就会将其刷入到磁盘中的redo log文件中去,可想而知,redo log文件就是由一个个的redo log block组成。
如果是执行事务,可能就会写多个redo log日志,他们被称为redo log group,可能一个redo log block放不下,那么就会放到两个甚至多个redo log block中,然后事务提交后就会将这一组redo log所在的redo log block都刷入磁盘中。
大致就是上面这样一个流程,相信大家看完后多多少少应该能理解上述组件存在的意义是什么
四、如果redo log文件写不下会怎么样?
我们现在知道,随着系统的不断运行,不断地写日志,redo log文件会越来越大,万一redo log文件写不下该会怎么办呢?这个其实不用太担心,只需要接着写下一个redo log文件即可,如果又写满了呢?难道继续写下一个redo log一直这样下去吗?答案是否定的,毕竟磁盘空间有限。
默认情况下,在磁盘上由两个名 为 ib_logfile0
和 的 ib_logfile1
的两个文件组成,MySQL以循环写入的方式写redo log日志,说白了就是ib_logfile0
写完了后就会写ib_logfile1
,ib_logfile1
写完了就写ib_logfile0
,如此往复。
每个文件的默认大小为48MB,可以通过编辑my.cnf中的 innodb_log_file_size
变量来配置文件大小,如果想增加文件数量,可以修改innodb_log_files_in_group
变量来进行配置,最大为100,文件存储的位置可以通过修改innodb_log_group_home_dir
变量来配置
五、小结
本篇文章想你介绍了redo log的日志格式,后面介绍了redo log block组件和log buffer组件以及他们的作用和刷盘机制、最后还介绍了redo log相关的一些配置,除了知道了原理,而且还0能够根据需要去进行合理的配置。
希望本篇文章能对你学习redo log有所帮助,如有问题欢迎指出