MySQL日志之重作日志redo

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字节。
日志主体包含如下内容:


app

  • 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设置刷盘async

  • 发出commit动做时。已经说明过,commit发出后是否刷日志由变量 innodb_flush_log_at_trx_commit 控制。函数

  • 每秒刷一次。这个刷日志的频率由变量 innodb_flush_log_at_timeout 值决定,默认是1秒。要注意,这个刷日志频率和commit动做无关。性能

  • 当log buffer中已经使用的内存超过一半时。加密

  • 当有checkpoint时,checkpoint在必定程度上表明了刷到磁盘时日志所处的LSN位置。spa

脏数据页刷盘
  在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表示是否加密