redo的场景
事务的持久性要求对于一个已经提交的事务,无论提交后发生什么情况,对数据库的更改都不能丢失。
但是在MySQL的InnoDB引擎中,无论对于数据的访问还是更新,都是在被称为buffer pool
的缓冲池中进行了的。
事务提交,意味着数据被写入内存中缓冲池,还未落地到磁盘,
那么当服务器发生故障或者断电等异常后,会导致缓冲池中的数据丢失
,违背了持久性。
那么是否可以在提交事务之前,将修改的数据直接刷新到磁盘来避免?
原则上上述简单粗暴的方法可以解决问题,但是会带来两个问题:
- buffer pool的引入就是为了缓解引擎与磁盘之间的速度差,减少磁盘IO,提高处理速度。
剔除掉缓冲池解决掉持久性问题,但是带来更大的性能问题
,得不偿失。 - InnoDB以页(默认16K)来管理
存储空间
,如果修改的页面的一个字节,也要刷新16K的数据,简直太浪费
了。
针对上面的场景,提出了redo来解决:
- 磁盘随机IO带来的速度问题,通过
redo日志文件的顺序写入
来解决。 - redo日志只需要记录表空间ID、页号、偏移量以及需要更新的值等,这些数据所需
空间很小
redo的组成
redo log buffer
类似于buffer pool的作用,redo也引入了名为redo log buffer的缓冲区,易失性。
通过参数innodb_log_buffer_size
可以修改缓冲区的大小,默认16M。
缓冲区被划分为大小为512字节的redo log block,redo日志被顺序写入到这些block中。
redo log
redo日志是以文件组的形式管理的,默认是在MySQL数据目录下(使用show variables like "datadir"
查看)。
目录下ib_logfile0和ib_logfile1即为redo日志文件,写入日志时,首先写入ib_logfile0,如果写满在写入ib_logfile1。
依此类推,当最后一个文件也写满时,再重新写入到ib_logfile0。在这里就会考虑到日志文件中的淘汰机制(checkpoint机制
),保证不覆盖掉有用的日志。
可以使用MySQL参数来修改与redo log相关的配置:
innodb_log_file_size
:每个redo日志文件的大小,默认48M;
innodb_log_group_home_dir
:redo日志文件存储目录,默认数据目录;
innodb_log_files_in_group
:redo日志文件数量,默认2。
redo流程
- 磁盘中的数据经过page cache,读取到buffer pool中;
- MySQL直接修改buffer pool中的数据,同时产出redo日志;
- 事务提交时,先将redo日志写入到redo log buffer(WAL机制),是否写入到磁盘取决于刷盘策略的配置。
- 后台线程将buffer pool中的脏页进行刷盘。
刷盘策略
redo log buffer的redo日志会根据不同情况和配置,刷新到磁盘上,实现真正的持久化。
- log buffer的容量使用占比大约为一半时,就会触发刷盘
- 后台线程,每秒一次刷盘(并不保证100%的‘每秒’)
- 服务器正常关闭时,触发刷盘
- 事务提交时,根据
innodb-flush-log-at-trx-commit
参数配置选择不同的刷盘策略:-
innodb-flush-log-at-trx-commit=1
(默认):
当事务提交时,将log buffer中的数据写入到page cache,并调用fsync进行刷盘,效率最低。不会丢失数据。 -
innodb-flush-log-at-trx-commit=0
:
当事务提交时,log buffer中的数据写入到page cache和刷盘都以后后台线程,效率最高。可能会丢失数据。 -
innodb-flush-log-at-trx-commit=2
:
当事务提交时,只将log buffer中的数据写入到page cache,刷盘依靠后台线程每秒种刷盘一次。可能会丢失数据。
-
checkpoint机制
redo log的文件组是按顺序写入,当写完最后一个文件时,会再从第一个文件开始写入。
在这种情况下,如果redo log中日志对应的脏页还未刷到磁盘上,那么在系统崩溃时就会导致数据丢失。
因此,需要记录redo日志中已经刷到磁盘的记录,保证循环写不会覆盖掉有用的数据。这个功能就需要checkpoint机制来实现。
checkpoint_lsn
:全局变量,系统中可以被覆盖的redo日志总量。
- 当脏页被刷到磁盘时,将当前系统中被最早修改的脏页对应的修改值复制给checkpoint_lsn。
- 将checkpoint_lsn与当前redo日志文件偏移量等信息写入到日志文件管理信息中。
经过上两步,即为一次checkpoint。
崩溃恢复
- 第一步,确认恢复的起点。
- checkpoint机制保证checkpoint_lsn之前的脏页已全部刷盘,那么之后的数据就是我们要恢复的。
- 第二步,确认恢复的终点。
- 通过日志文件block的header中的信息可以知道每个block使用的字节空间,从而确定终点
- 第三步,依次扫描日志,将对应的页恢复。
- 通过hash表加快恢复速度:将表空间ID和页号取hash,这样同样表空间和页下的redo日志就可一次查询并恢复。