InnoDB会运行一些后台任务,其中就包括刷脏页任务。脏页是指已经被修改,但是还没写到磁盘上的数据文件中的数据页。
在MySQL8.0中,刷脏页任务是由page cleaner线程处理的。page cleaner线程的数量由变量innodb_page_cleaners控制,其默认值是4。但是,如果innodb_page_cleaners的值超过buffer pool实例的数量,其就会被自动设置成innodb_buffer_pool_instances的值。
当脏页比例达到由变量innodb_max_dirty_pages_pct_lwm定义的低水位值时,开始进行刷脏的初始化操作。低水位的默认值是10%。如果innodb_max_dirty_pages_pct_lwm的值为0,就会禁用这种早起刷脏行为。
innodb_max_dirty_pages_pct_lwm阈值的目的,是为了控制buffer pool中的脏页比例,避免其达到innodb_max_dirty_pages_pct的值(默认值是90)。如果达到innodb_max_dirty_pages_pct的值,InnoDB就会积极刷脏。
innodb_max_dirty_pages_pct_lwm的值,应该总是比innodb_max_dirty_pages_pct小。
其它一些可以用来进行buffer pool刷脏调优的变量:
- innodb_flush_neighbors,控制刷某个脏页时,是否对同一个区(extent)内的其它脏页也进行刷脏操作。
- innodb_flush_neighbors的默认值是0.不会刷同一个区内的其它脏页。这个配置适用于非旋转式设备(SSD)。对于这种设备来说,查找时间不是一个重要因素。
- 值为1,会刷同一个区内的连续脏页。
- 值为2,会刷同一个区内的脏页。 如果表数据存储在传统的HDD设备上,相比在不同的时间点单独刷脏,刷相邻脏页可以减少磁盘I/O压力(主要是减少了寻找时间)。如果表数据是存储在SSD设备上,你可以禁用这个设置,这样可以让写操作离散。
- innodb_lru_scan_depth,控制page cleaner线程在每个buffer pool实例的lru链表中查找脏页时的深度。这个操作由page cleaner在后台运行,每秒1次。 对于大部分场景来说,可以将其修改为比默认值小的值。如果值过高,可能会带来性能问题。在特定场景下,只有当你有充足的I/O能力时,才可以考虑增加这个变量的值。相反的,如果是写密集型工作负载,I/O能力饱和,降低这个变量的值,尤其是在buffer pool比较大时。 在调整这个变量值时,先从一个较小的值开始,逐步调大,避免出现无空闲页可用的情况。修改buffer pool实例数量时,考虑调整这个变量的值,因为 innodb_lru_scan_depth * innodb_buffer_pool_instances决定了page cleaner每条执行后台任务时的工作量。
innodb_flush_neighbors 和 innodb_lru_scan_depth主要用于写密集型工作负载。当DML操作压力大时,如果不积极刷脏,刷脏操作可能会有延迟,如果刷脏过于积极,可能会造成磁盘I/O负载饱和。什么是理想设置,取决于你的工作负载,数据访问模式,以及存储设置(例如,数据是存储在HDD设备还是SSD设备上)。
自适应刷脏 #
InnoDB会基于redo log的生成速度和当前的刷脏速率,使用自适应刷脏算法来动态调整刷脏速率。其目的是通过确保刷脏活动与工作负载同步来使性能曲线平滑。自动调整刷脏速率可以避免因刷脏而造成I/O压力徒增,进而造成吞吐量下降。
例如,通常与产生大量redo条目相关的密集写工作负载相关的尖峰checkpoints,可能会导致吞吐量突然变化。当InnoDB想要重用某个日志文件的某块区域时,尖峰检查点就会出现。在重用之前,这块区域中redo记录相关的脏页必须都已经被刷新到磁盘上。如果日志文件写满,尖峰检查点出现,会导致短暂的吞吐量突然下降。即使没有达到 innodb_max_dirty_pages_pct的值,也可能会出现这种情况。
通过追踪脏页数量和redo日志生成速率,自适应刷脏算法有助于避免这种场景。基于这些信息,自适应算法会判断每秒要刷多少脏页,从而管控工作负载的突然增加。
innodb_adaptive_flushing_lwm 定义了redo日志能力的低水位。超过了这个值,就会启用自适应刷脏,即使innodb_adaptive_flushing变量被禁用。
内部性能表明,自适应刷脏算法不仅可以再某段时间内保持吞吐量,还可以提高整体吞吐量。但是,自适应刷脏会显著地影响工作负载的磁盘I/O,可能不适用于所有场景。在redo log有写满的风险时,这种算法的收益最大。如果自适应刷脏不适用于你的工作负载,可以禁用它。是否开启自适应刷脏由innodb_adaptive_flushing变量控制,默认开启。
innodb_flushing_avg_loops定义了InnoDB保存的刷脏状态计算结果的历史快照的迭代次数,控制着自适应刷脏算法对突发工作负载的响应速度。 innodb_flushing_avg_loops大,意味着InnoDB将之前计算的快照保存的更久,自适应算法响应更慢。如果值比较大,需要确保redo日志利用率没有达到75%(硬编码限制), innodb_max_dirty_pages_pct值决定了脏页数量。
要知道,如果刷脏延迟,buffer pool的刷脏速率可能会超过innodb_io_capacity定义的InnoDB能用的磁盘I/O能力。 innodb_io_capacity_max定义了I/O能力上限,所以I/O活动尖刺不会消耗掉整个服务器所有的I/O能力。
innodb_io_capacity适用于所有的buffer pool实例。在刷脏时,I/O能力被均匀分配给所有实例。
空闲期间限制刷脏 #
从MySQL 8.0.18开始,你可以使用innodb_idle_flush_pct来限制空闲时期的刷脏速率。空闲时期,没有数据页被修改。 innodb_io_capacity setting控制着InnoDB每秒磁盘I/O操作次数上限,而innodb_idle_flush_pct是其 一个百分比。innodb_idle_flush_pct的默认值是100。要限制空闲时期刷脏,将innodb_idle_flush_pct设置成被100小的值。
控制空闲时期刷脏,有助于延长存储设备使用寿命。副作用是长期空闲后关机时,花费时间更长,服务器崩溃后恢复时间更长。