前言
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(预写日志)
策略,即当
事务提交时
,
先写重做日志
,然后
再择时
将脏页
写入磁盘
。
InnoDB存储引擎会首先将
数据变更
的重做日志信息
先放入重做日志缓冲中
,然后再按照一定频率将其刷新到重做日志文件。重做日志缓冲一般不需要设置得很大,因为一般情况每一秒钟都会讲重做日志缓冲刷新到日志文件中。可通过配置参数
innodb_log_buffer_size
控制,默认为
8MB
。
Redo 写盘操作
我们所熟悉的参数
innodb_flush_log_at_trx_commit
作用于事务提交时,这也是最常见的场景:
- 当设置该值为
时,1
,这是最安全的配置,即使宕机也不会丢失事务每次事务提交都要做一次fsync
- 当设置为
时,则在事务提交时只做write操作,只2
,因此实例crash不会丢失事务,但宕机则可能丢失事务保证写到系统的page cache
- 当设置为 时,事务提交不会触发redo写操作,而是留给
,因此实例crash将后台线程每秒一次的刷盘操作
最多丢失1秒钟内的事务
InnoDB的
写入机制
大致入下图所示。
显然对性能的影响是随着持久化程度的增加而增加的。通常我们
建议在日常
场景将该值
设置为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线程控制,master thread checkpoint
刷入每秒或每10秒
的脏页到磁盘。一定比例
-
:从flush_lru_list checkpoint
开始可通过MySQL5.6
变量指定专门负责脏页刷盘的innodb_page_cleaners
,该线程的page cleaner线程的个数
是为了目的
。保证lru列表有可用的空闲页
-
:async/sync flush checkpoint
还是同步刷盘
。例如还有异步刷盘
没刷到磁盘(非常多是多少,有非常多的脏页
),这时候会选择比例控制
刷到磁盘,但这很少出现;如果同步
,可以选择脏页不是很多
刷到磁盘,如果脏页异步
,可以很少
脏页到磁盘暂时不刷
-
:dirty page too much checkpoint
时脏页太多
检查点,目的是强制触发
。too much的比例由变量为了保证缓存有足够的空闲空间
控制,MySQL 5.6innodb_max_dirty_pages_pct
的值为默认
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