天天看点

Mysql知识延展(五)Redo Log日志

前言

InnoDB 有两块非常重要的日志,一个是

undo log

,它用来保证

事务的原子性

以及

InnoDB的MVCC

,另外一个就是是

redo log

,它用来保证

事务的持久性

InnoDB记录了对

数据文件的物理更改

,并保证总是

日志先行

,也就是所谓的WAL,即在持久化数据文件前,保证之前的redo日志已经写到磁盘。

LSN

(log sequence number) 用于记录

日志序号

,它是一个不断递增的 unsigned long long 类型整数。在 InnoDB 的日志系统中,LSN 无处不在,它既用于

表示修改脏页时的日志序号

,也用于记录checkpoint,通过LSN,可以具体的

定位

到其在

redo log文件

中的位置。

为了管理脏页,在

Buffer Pool

每个instance

上都维持了一个

flush list

,flush list 上的 page 按照修改这些 page 的LSN号进行排序。因此定期做redo checkpoint点时,选择的 LSN 总是所有 bp instance 的 flush list 上最老的那个page(拥有最小的LSN)。由于采用WAL的策略,每次事务提交时需要持久化 redo log 才能保证事务不丢。而延迟刷脏页则起到了合并多次修改的效果,避免频繁写数据文件造成的性能问题。

本文的分析基于最新的MySQL 5.7.7-RC版本。

Redo log

InnoDB的redo log可以通过参数

innodb_log_files_in_group

配置成多个文件,另外一个参数

innodb_log_file_size

表示每个文件的大小。

因此总的redo log大小为innodb_log_files_in_group * innodb_log_file_size。

Redo log文件以

ib_logfile[number]命名

,日志

目录

可以通过参数

innodb_log_group_home_dir

控制。Redo log 以

顺序

的方式

写入文件

,写

满时

回溯到第一个文件

,进行

覆盖写

。(但在做redo checkpoint时,也会更新第一个日志文件的头部checkpoint标记,所以严格来讲也不算顺序写)。在

覆盖写之前

,总是要

保证对应的脏页

已经

刷到了磁盘

。在非常

大的负载

下,Redo log可能产生的速度非常快,导致

频繁的刷脏操作

,进而

导致性能下降

,通常在redo checkpoint的日志超过

文件总大小

76%

之后,InnoDB 认为这可能是个不安全的点,会

强制的preflush脏页

导致大量用户线程停住

如果可预期会有这样的场景:

建议

调大redo log文件

的大小。可以做一次干净的

shutdown

,然后

修改Redo log配置

重启

实例

Redo Log 如果要

存储数据

先存储数据的日志

,如果发生宕机导致数据丢失,就通过重做日志进行数据恢复。重做日志保证了数据的

可靠性

,InnoDB采用了

Write Ahead Log(预写日志)

策略,即当

事务提交时

先写重做日志

,然后

再择时

将脏页

写入磁盘

Mysql知识延展(五)Redo Log日志

InnoDB存储引擎会首先将

数据变更

的重做日志信息

先放入重做日志缓冲中

,然后再按照一定频率将其刷新到重做日志文件。重做日志缓冲一般不需要设置得很大,因为一般情况每一秒钟都会讲重做日志缓冲刷新到日志文件中。可通过配置参数

innodb_log_buffer_size

控制,默认为

8MB

Redo 写盘操作

我们所熟悉的参数

innodb_flush_log_at_trx_commit

作用于事务提交时,这也是最常见的场景:

  • 当设置该值为

    1

    时,

    每次事务提交都要做一次fsync

    ,这是最安全的配置,即使宕机也不会丢失事务
  • 当设置为

    2

    时,则在事务提交时只做write操作,只

    保证写到系统的page cache

    ,因此实例crash不会丢失事务,但宕机则可能丢失事务
  • 当设置为 时,事务提交不会触发redo写操作,而是留给

    后台线程每秒一次的刷盘操作

    ,因此实例crash将

    最多丢失1秒钟内的事务

InnoDB的

写入机制

大致入下图所示。

Mysql知识延展(五)Redo Log日志

显然对性能的影响是随着持久化程度的增加而增加的。通常我们

建议在日常

场景将该值

设置为1

,但在系统

高峰期临时修改成2

以应对大负载。

由于

各个事务

可以

交叉

的将

事务日志拷贝到log buffer

中,因而

一次事务提交

触发的

redo到

文件

可能

隐式的

帮别的线程“顺便”也写了redo log

,从而达到

group commit

的效果。

Redo checkpoint

check point

目的

是为了

定期

data page buffer

内容转储到data file

。在转储时,会

记录check point

发生的

”时刻“

。在

故障恢复

时候,

只需要redo/undo最近的一次checkpoint之后的操作

InnoDB的redo log采用

覆盖循环写

的方式,而

不是

拥有

无限

的redo

空间

;即使拥有理论上极大的redo log空间,

为了

从崩溃中

快速恢复

,及时做

checkpoint

也是非常有

必要的

在innodb中,数据刷盘的规则只有一个:checkpoint。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后,就强制刷一部分脏页到磁盘。

      由于刷脏页需要一定的时间来完成,所以

      记录检查点的位置

      是在每次

      刷盘结束之后

      才在

      redo log中标记

      的。

InnoDB的

master线程

大约

每隔10秒

会做一次

redo checkpoint

,但不会去preflush脏页来推进checkpoint点。

通常普通的低压力负载下,

page cleaner

线程的刷脏速度足以保证可作为检查点的lsn被及时的推进。但如果系统负载很高时,

redo log推进速度过快

,而page cleaner来不及刷脏,这时候就会出现

用户线程陷入同步刷脏并做checkpoint的境地

,这种策略的目的是为了保证redo log能够安全的写入文件而不会覆盖最近的检查点。

参考:https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html

继续阅读