1.名词解释
脏页:当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为“脏页”。
干净页:内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为“干净页”。
LSN:称为日志的逻辑序列号(log sequence number),在innodb存储引擎中,lsn占用8个字节。LSN的值会随着日志的写入而逐渐增大。事务中更新操作会产生一个新的LSN。LSN不仅存在于redo log中,还存在于数据页中。
2.产生原因
MySQL有两个重要组成部分,内存和磁盘。
磁盘是永久存储,内存中的数据则有可能丢失。但内存上修改的数据都会记录到redo log,所以即使丢失,也能从redo log中找回。
MySQL的IO特别耗费性能,所以很多操作在内存中进行的,并不立即刷到磁盘。
例如,更新一行数据时
-
如果内存中不存在该行数据,则从磁盘中找到该行所在的数据页
-
将整页的数据读入内存(如果change buffer中记录了该页的更改信息,会在内存中进行更新)
-
在内存中对该行进行更新,整个更新操作完成
这个场景下,内存中的数据和磁盘中的数据已经不一致了,便产生了脏页。
当读取该行数据时,因为内存中的数据是最新的,直接读取内存数据返回,省去读磁盘的时间,提升性能。
3.内存数据和redo log的关系
要明白刷脏页的流程,首先要理解内存中的数据和redo log中数据的关系。
-
内存中的数据和redo log中的数据是一致的
-
如何判断是否为脏页:
-
redo log中记录了LSN
-
redo log中的checkpoint位于某个LSN,意味着该LSN之前的数据都已写入磁盘
-
内存中干净页如有修改,修改后该页的LSN大于 checkpoint 的LSN,则在写redo log的同时该页也会被标记为脏页记录到脏页列表
脏页本质是内存数据页跟磁盘数据页内容不一致**。所以内存数据页刷盘或者redo log的checkpoint向后移动,都会将脏页变为干净页。**
4.刷脏页时机
刷脏页(flush)的时机如下:
- InnoDB 的 redo log 已满
- 这时候系统会停止所有更新操作,把 checkpoint 往前推进,以便 redo log 留出空间可以继续写。对checkpoint 移动范围内的日志,对应的所有脏页都 flush到磁盘上
- 当需要新的内存页,而内存不够用的时候
- 淘汰一些内存数据页,空出内存给别的数据页使用。如果淘汰的是“脏页”,就要先将脏页写到磁盘
-
MySQL 认为系统“空闲”时,见缝插针地找时间,只要有机会就刷一点“脏页”
-
MySQL 正常关闭时,会把内存的脏页都 flush 到磁盘上
刷盘的脏页的LSN可能比redo log中checkpoint的LSN高,但没有影响,当redo log在“重放”的时候,如果一个数据页已经是刷过的,会识别出来并跳过。
5.刷脏页影响
需要根据不同的时机来看对性能的影响。
空闲和正常关闭情况下,不会太关注性能。所以主要看第一种和第二种情况。
- 尽量不要出现redo log写满的情况,一旦发生,所有更新操作都会停止
- redo log大小需要合理设置
- 内存页有三种状态:未使用的、干净页、脏页。当要读入的数据页没有在内存的时候,就必须到缓冲池中申请一个数据页。这时候只能把最久不使用的数据页从内存中淘汰掉:如果要淘汰的是一个干净页,就直接释放出来复用;但如果是脏页呢,就必须将脏页先刷到磁盘,变成干净页后才能复用。如果一个查询要淘汰的脏页个数太多,会导致查询的响应时间明显变长。
- 合理控制脏页比例
6.控制刷脏页
-
innodb_io_capacity:合理设置 innodb_io_capacity,这个值告诉innodb可以以多快速度写磁盘。如果系统要刷脏页了,innodb会参考这个值,计算出一个刷脏页的速度。之所以不按照这个值来刷脏页,是因为不能将所有能力全用于刷脏页,耽误处理正常请求。
-
Innodb会根据现在脏页的比例(可通过 innodb_max_dirty_pages_pct控制)和redo log剩余空间,计算出刷盘速度百分比R,从而计算出刷脏页速度=innodb_io_capacity*R%
所以为防止刷脏页影响性能,要合理设置redo log大小、innodb_io_capacity、innodb_max_dirty_pages_pct。
资料
- MySQL45讲
最后
大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)
我的个人博客为:https://shidawuhen.github.io/
往期文章回顾: