redo log是什么
redo log 是一种用于存储对mysql数据修改记录一种文件。
在使用时,我们对mysql的操作会记录在内存页上,只要保存在内存上就不是安全的,必然要写回磁盘中,但这样会有下面的两个问题:
- 每修改一处就写回磁盘就过于浪费资源。
- 一个操作可能会修改了多张表,如果每个表都要修改的话就是随机IO了,资源的消耗进一步加大。
所以redo log就被设计出来,用于记录修改的信息内容,防止系统崩溃导致数据无法修复,同时避免了上述的两种问题,redo log只会顺序记录下修改的内容,这样存储到磁盘中就全部都是发生变化的数据,并且由于是顺序IO,速度也会比随机IO快。
其格式(一个修改记录的)整体如下图所示:
- type,表示该记录的类型,在mysql中redo log的类型有好几十种
- space ID,表空间的ID
- page number ,表示发生修改的页号
- data,表示发生修改的数据
无论何种情况redo log总能记录下我们修改的内容。
Mini-Transaction
在mysql中我们一个插入操作可能会涉及到修改很多张表(比如导致了页分裂,使得索引页要多加一行记录),从整体上看,所有的相关记录都是这一条语句的,所以这些记录如果没有一起执行的话就会造成问题,因此redo log被分成了若干组,每组都是一个原子单位,要么都执行成功,要么都不成功。
在mysql中,对底层页面中的一次原子访问的过程称之为一个 Mini-Transaction ,简称 mtr。
一个 mtr 可以包含一组 redo 日志,在进行崩溃恢复时这一组 redo 日志作为一个不可分割的整体。
redo log的写
在mysql中数据的写都是会存在内存中的,redo log也不例外,一个redo log内存页面的大小是512kb,除去一个页面的开头和结尾用户记录必要信息的内容外,可以有将近500kb的存储内容。
mysql会申请开辟出部分内存来存放多个redo log 日志页,同时有buf_free这个变量来指示记录数据写到了哪个位置,也就是buf_free后面的空间都是还未使用的。还有个buf_next_to_write变量来标记哪些内存写入磁盘了,它后面的内容是还没写入到磁盘的数据。
在记录时第一个页面满了就会自动写入到第二个页面中,redo log是以组为单位记录的,多个事务实时执行会同时产生多个mtr,在存储时是不管事务顺序的,也就是相邻的mtr记录可能不是同一个事务的。
mysql会维护一个lsn的全局变量,来记录每个redo log。lsn是递增的,所以lsn越小说明其对应的redo log的操作执行的越早。
写入磁盘
在合适时机,mysql会将redolog写入到磁盘中,在磁盘中的redo log 会存储在ib_logfile的文件中,这个文件默认有两个(ib_logfile0和ib_logfile1)。每个的空间大小是有限的,所以redo log是在有限的空间中循环写。
有个叫buf_next_to_write 的全局变量,标记当前 log buffer 中已经有哪些日志被刷新到磁盘中了。磁盘中有个flushed_to_disk_lsn变量记录当前磁盘中写入的位置。
redo log只是用来记录数据的修改记录的,当其对应的脏页写入到磁盘后该redo log记录也就没有必要继续存在磁盘或内存上了。在内存中修改过数据的脏页的对应数据块会形成一个链表,数据块上记录了对这个脏页有关联的最大和最小lsn。分别对应第一次修改的lsn和最后一次修改的lsn的值。
那么执行redo log覆盖的时候只要判断链表中最早被修改的页面数据块对应的最小lsn就可以判断区分了。然后在计算cheackpoint的值,用来指向磁盘中什么记录可以覆盖掉。 所以cheackpiont 和 buf_next_to_write之间的值就是还没写入磁盘的磁盘redo log记录。
在ib_logfile文件中也是分成多个block页面的的,一个页面和内存中的redo log大小一样。同时每个ib_logfile文件的开头还有4个block用于存储额外的信息。