文章目录

  • 1.1 为什么需要redo
  • 1.2 redo log 结构
  • 1.3 刷盘规则
  • 1.4 LSN
  • 1.5 相关变量


1.1 为什么需要redo

  数据库为了取得更好的读写性能,会将数据缓存在内存中,对磁盘数据的修改也会落后于内存,这时如果进程或机器崩溃,会导致内存数据丢失,为了保证数据库本身的一致性和持久性,InnoDB引入了redo log。修改数据页之前需要先将修改的内容记录到redo log中,并保证redo log早于对应的数据页落盘,也就是常说的预写式日志(WAL)。当故障发生导致内存数据丢失后,InnoDB会在重启时,通过重放REDO,将Page恢复到崩溃前的状态。

1.2 redo log 结构

  redo log是物理日志,记录的是数据页的物理修改,用来恢复提交后的物理数据页。
  redo log包括两部分:

  • 内存中的redo log buffer(内存数据,会丢失);
  • 磁盘上的redo log file(磁盘数据,持久化);

日志块
  innodb存储引擎中,redo log以块为单位进行存储的,每个块占512字节,称为redo log block。所以不管是log buffer中还是os buffer中以及redo log file中,都是这样以512字节的块存储的,一个页内可以存放非常多的log block。
  每个redo log block由3部分组成:日志块头、日志块尾和日志主体。其中日志块头占用12字节,日志块尾占用8字节,所以每个redo log block的日志主体部分只有512-12-8=492字节。
日志主体包含以下内容:

  • redo_log_type:占用1个字节,表示redo log的日志类型。
  • space:表示表空间的ID,采用压缩的方式后,占用的空间可能小于4字节。
  • page_no:表示页的偏移量,同样是压缩过的。
  • redo_log_body表示每个重做日志的数据部分,恢复时会调用相应的函数进行解析。

1.3 刷盘规则

  内存中未刷到磁盘的数据称为脏数据。log buffer中未刷到磁盘的日志称为脏日志。由于数据和日志都以页的形式存在,所以脏页分为脏数据和脏日志。
redo 脏日志刷盘

  • 按照innodb_flush_log_at_trx_commit设置刷盘
  • 发出commit动作时。已经说明过,commit发出后是否刷日志由变量 innodb_flush_log_at_trx_commit 控制。
  • 每秒刷一次。这个刷日志的频率由变量 innodb_flush_log_at_timeout 值决定,默认是1秒。要注意,这个刷日志频率和commit动作无关。
  • 当log buffer中已经使用的内存超过一半时。
  • 当有checkpoint时,checkpoint在一定程度上代表了刷到磁盘时日志所处的LSN位置。

脏数据页刷盘
  在innodb中,数据刷盘依赖于checkpoint。但是触发checkpoint的情况却有几种。checkpoint触发后,会将buffer中脏数据页和脏日志页都刷到磁盘。

innodb存储引擎中checkpoint分为两种:

  • sharp checkpoint:在重用redo log文件(例如切换日志文件)的时候,将所有已记录到redo log中对应的脏数据刷到磁盘。
  • fuzzy checkpoint:一次只刷一小部分的日志到磁盘,而非将所有脏日志刷盘。有以下几种情况会触发该检查点:
  • master thread checkpoint:由master线程控制,每秒或每10秒刷入一定比例的脏页到磁盘。
  • flush_lru_list checkpoint:从MySQL5.6开始可通过 innodb_page_cleaners 变量指定专门负责脏页刷盘的page cleaner线程的个数,该线程的目的是为了保证lru列表有可用的空闲页。
  • async/sync flush checkpoint:同步刷盘还是异步刷盘。例如还有非常多的脏页没刷到磁盘(非常多是多少,有比例控制),这时候会选择同步刷到磁盘,但这很少出现;如果脏页不是很多,可以选择异步刷到磁盘,如果脏页很少,可以暂时不刷脏页到磁盘
  • dirty page too much checkpoint:脏页太多时强制触发检查点,目的是为了保证缓存有足够的空闲空间。too much的比例由变量 innodb_max_dirty_pages_pct 控制,MySQL 5.6默认的值为75,即当脏页占缓冲池的百分之75后,就强制刷一部分脏页到磁盘。

  MySQL停止时是否将脏数据和脏日志刷入磁盘,由变量innodb_fast_shutdown={ 0|1|2 }控制,默认值为1,即停止时只做一部分purge,忽略大多数flush操作(但至少会刷日志),在下次启动的时候再flush剩余的内容,实现fast shutdown。

1.4 LSN

  LSN称为日志的逻辑序列号(log sequence number),在innodb存储引擎中,lsn占用8个字节。LSN的值会随着日志的写入而逐渐增大。LSN不仅存在于redo log中,还存在于数据页中,在每个数据页的头部,有一个fil_page_lsn记录了当前页最终的LSN值是多少。通过数据页中的LSN值和redo log中的LSN值比较,如果页中的LSN值小于redo log中LSN值,则表示数据丢失了一部分,这时候可以通过redo log的记录来恢复到redo log中记录的LSN值时的状态。

show engine innodb status\G

---
LOG
---
#当前的log buffer中的lsn
Log sequence number          498732361
#Link_buf初始化的lsn
Log buffer assigned up to    498732361
#Link_buf完成的lsn
Log buffer completed up to   498732361
#到了这个lsn 为止, 之前的log buffer 里面都不会有空洞
Log written up to            498732361
刷到redo log file on disk中的lsn
Log flushed up to            498732361
#redo log 对应的dirty page 已经添加到buffer pool 的flush list
Added dirty pages up to      498732361
#已经刷到磁盘数据页上的LSN
Pages flushed up to          498732361
#上一次检查点所在位置的LSN
Last checkpoint at           498732361
1092 log i/o's done, 0.00 log i/o's/second

Log sequence number>=Log buffer assigned up to>=Log buffer completed up to>=Log written up to>=Log flushed up to>=Added dirty pages up to>=Pages flushed up to>=Last checkpoint at

1.5 相关变量

  • innodb_flush_log_at_trx_commit指定何时将事务日志刷到磁盘,默认为1。
  • 0表示每秒将"log buffer"同步到"os buffer"且从"os buffer"刷到磁盘日志文件中。
  • 1表示每事务提交都将"log buffer"同步到"os buffer"且从"os buffer"刷到磁盘日志文件中。
  • 2表示每事务提交都将"log buffer"同步到"os buffer"但每秒才从"os buffer"刷到磁盘日志文件中。
  • innodb_flush_log_at_timeout 定义了每次日志刷新的时间,与innodb_flush_log_at_trx_commit 配合使用
  • innodb_log_buffer_size表示log buffer的大小
  • innodb_log_file_size事务日志的大小
  • innodb_log_checksums表示在写入redo log到文件之前,redo log的每一个block都需要加上checksum校验位,以防止apply损坏redo log
  • innodb_log_files_group事务日志组中的事务日志文件个数
  • innodb_log_group_home_dir事务日志组路径,当前目录表示数据目录
  • innodb_redo_log_archive_dirs是redo归档目录
  • innodb_redo_log_encrypt表示是否加密