2026/4/18 11:16:58
网站建设
项目流程
鄂州免费设计网站建设,代理网络软件,骨干专业群建设任务书网站,网创项目平台#x1f504; InnoDB Page Cleaner 刷脏页机制深度解析
#x1f4ca; 脏页#xff08;Dirty Page#xff09;是什么#xff1f;
1. 脏页定义
-- 脏页 内存中被修改但未写入磁盘的数据页
-- 在内存中的版本#xff08;新#xff09;≠ 在磁盘中的版本#xff08;旧InnoDB Page Cleaner 刷脏页机制深度解析脏页Dirty Page是什么1. 脏页定义-- 脏页 内存中被修改但未写入磁盘的数据页-- 在内存中的版本新≠ 在磁盘中的版本旧2. 脏页生命周期┌─────────────────────────────────────────────────────┐ │ 脏页生命周期 │ ├─────────────────────────────────────────────────────┤ │ 1. 从磁盘读取干净页到缓冲池 │ │ 2. 应用程序修改页内数据 │ │ 3. 标记为脏页内存 vs 磁盘不一致 │ │ 4. Page Cleaner 将脏页写入磁盘 │ │ 5. 标记为干净页内存和磁盘一致 │ └─────────────────────────────────────────────────────┘3. 可视化脏页状态# 查看脏页统计mysql -e SELECT 缓冲池总页数 AS 指标, VARIABLE_VALUE AS 值, 页 AS 单位 FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_buffer_pool_pages_total UNION ALL SELECT 当前脏页数量, VARIABLE_VALUE, 页 FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_buffer_pool_pages_dirty UNION ALL SELECT 脏页比例, CONCAT(ROUND( (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_buffer_pool_pages_dirty) / (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_buffer_pool_pages_total) * 100, 2), %), ORDER BY 1; 脏页在 MySQL 中的产生机制1. 触发脏页产生的操作-- 1. DML 操作INSERT/UPDATE/DELETEUPDATEusersSETlast_loginNOW()WHEREid1;-- ↑ 修改内存中的页标记为脏页-- 2. 索引变更ALTERTABLEordersADDINDEXidx_created(created_at);-- ↑ 构建索引时产生大量脏页-- 3. 批量数据操作INSERTINTOlog_tableSELECT*FROMhuge_table;-- ↑ 大规模数据插入产生大量脏页-- 4. 隐式产生-- 行格式转换、空间回收等内部操作也会产生脏页2. 监控脏页产生的脚本#!/bin/bash# monitor-dirty-page-generation.shecho 脏页生成监控 echo时间戳,总页数,脏页数,每秒新增脏页,刷新页数,未决刷新/tmp/dirty_page_log.csvLAST_DIRTY0LAST_FLUSHED0whiletrue;do# 获取当前状态CURRENT$(mysql -Nse SELECT(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAMEInnodb_buffer_pool_pages_total),(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAMEInnodb_buffer_pool_pages_dirty),(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAMEInnodb_buffer_pool_pages_flushed),(SELECT SUM(VARIABLE_VALUE)FROM performance_schema.global_status WHERE VARIABLE_NAME LIKEInnodb_buffer_pool_pending_flushes_%) 2/dev/null || echo 0000) TOTAL$(echo$CURRENT|awk{print $1})DIRTY$(echo$CURRENT|awk{print $2})FLUSHED$(echo$CURRENT|awk{print $3})PENDING$(echo$CURRENT|awk{print $4})# 计算速率 TIMESTAMP$(date%s)DIRTY_DELTA$((DIRTY-LAST_DIRTY))FLUSH_DELTA$((FLUSHED-LAST_FLUSHED))# 输出 echo $TIMESTAMP,$TOTAL,$DIRTY,$DIRTY_DELTA,$FLUSH_DELTA,$PENDING | \ tee -a /tmp/dirty_page_log.csv # 更新上一次的值 LAST_DIRTY$DIRTYLAST_FLUSHED$FLUSHED# 检查阈值 if [$DIRTY-gt$((TOTAL*75/100))]; then echo 警告: 脏页比例超过75%!当前:$((DIRTY*100/TOTAL))%fisleep1done⚙️Page Cleaner 刷脏页机制详解1. Page Cleaner 架构-- Page Cleaner 是多线程架构-- 每个线程负责一部分缓冲池实例的刷新-- 工作流程-- 1. 扫描 LRU 列表寻找旧脏页-- 2. 扫描 Flush 列表寻找检查点脏页-- 3. 批量写入到 doublewrite buffer-- 4. 写入到数据文件2. 刷新触发机制-- 三种触发方式-- 1. 自适应刷新Adaptive Flushing-- 基于redo日志生成速率和当前脏页比例-- 2. 同步刷新Sync Flushing-- 当脏页比例达到 innodb_max_dirty_pages_pct_lwm 时触发-- 3. 检查点刷新Checkpoint Flushing-- 保证redo日志空间可循环使用3. 刷新优先级-- 刷新顺序优先级从高到低-- 1. 最近最少使用LRU的脏页-- 2. 最老的脏页从Flush列表-- 3. 随机脏页当压力大时4. 监控刷新过程# 深度监控 Page Cleanermysql -e -- 查看刷新统计 SELECT LRU 列表刷新 AS 刷新类型, VARIABLE_VALUE AS 刷新页数 FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_buffer_pool_pages_made_not_young UNION ALL SELECT Flush 列表刷新, VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_buffer_pool_pages_flushed UNION ALL SELECT 每秒刷新速率, ROUND(VARIABLE_VALUE / (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Uptime), 2) FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_buffer_pool_pages_flushed; 提高刷新效率的全面策略1. MySQL 配置优化静态参数需要重启# /etc/my.cnf [mysqld] # 1. Page Cleaner 线程数等于或少于缓冲池实例数 innodb_page_cleaners 8 # 2. 缓冲池实例数提高并发刷新 innodb_buffer_pool_instances 8 # 3. 缓冲池大小足够大可减少频繁刷新 innodb_buffer_pool_size 64G # 4. 日志文件大小大日志减少检查点压力 innodb_log_file_size 4G innodb_log_files_in_group 3 # 5. 使用 O_DIRECT 绕过 OS 缓存 innodb_flush_method O_DIRECT动态参数在线调整-- 在线优化刷新效率SETGLOBALinnodb_io_capacity4000;-- 根据磁盘性能设置SETGLOBALinnodb_io_capacity_max8000;-- 峰值 I/O 能力SETGLOBALinnodb_lru_scan_depth1024;-- LRU 扫描深度SETGLOBALinnodb_max_dirty_pages_pct75;-- 最大脏页比例SETGLOBALinnodb_max_dirty_pages_pct_lwm50;-- 低水位线SETGLOBALinnodb_adaptive_flushingON;-- 启用自适应刷新SETGLOBALinnodb_adaptive_flushing_lwm10;-- 自适应刷新低水位SETGLOBALinnodb_flush_neighbors0;-- SSD 设为 02. 磁盘和 I/O 优化存储层优化# 1. 使用高性能存储# NVMe SSD SATA SSD HDD# 2. 配置 RAID如果使用 HDD# RAID 10 提供最佳性能和冗余# 3. 文件系统优化# 使用 XFS 或 ext4 with noatime,nodiratimemount|grep/var/lib/mysql# 理想选项noatime,nodiratime,barrier0,dataordered# 4. I/O 调度器优化echodeadline/sys/block/sda/queue/scheduler# 数据库负载# 或使用 noopSSD监控磁盘性能#!/bin/bash# monitor-disk-io-for-page-cleaner.shecho 磁盘 I/O 性能监控Page Cleaner 相关# 监控写入延迟关键指标iostat -dx25|awk BEGIN {print 设备,使用率%,平均等待(ms),平均服务(ms),每秒写入} NR3 $1 ~ /^sd|^nvme/ { printf %s,%.1f,%.1f,%.1f,%.1f\n, $1, $14, $10, $11, $5 }# 查看 InnoDB 相关的 I/O 统计mysql -e SELECT VARIABLE_NAME, VARIABLE_VALUE, CASE WHEN VARIABLE_NAME LIKE %write% THEN 写入 WHEN VARIABLE_NAME LIKE %read% THEN 读取 WHEN VARIABLE_NAME LIKE %sync% THEN 同步 ELSE 其他 END AS 类型 FROM performance_schema.global_status WHERE VARIABLE_NAME LIKE Innodb_data_% OR VARIABLE_NAME LIKE Innodb_os_log_% ORDER BY 类型, VARIABLE_NAME; 3. 应用层优化减少脏页生成-- 1. 批量提交事务STARTTRANSACTION;-- 多条更新语句UPDATEtable1SET...;UPDATEtable2SET...;COMMIT;-- 一次产生一组脏页-- 2. 合理使用事务大小-- 避免超大事务产生大量脏页-- 3. 优化 UPDATE/DELETE 模式-- 使用 LIMIT 分批更新UPDATElarge_tableSETstatusprocessedWHEREstatuspendingLIMIT1000;-- 4. 使用合适的数据类型-- 避免不必要的大字段更新监控事务模式#!/bin/bash# monitor-transaction-pattern.shecho 事务模式分析脏页生成相关# 监控长事务和大事务mysql -e SELECT trx_id, trx_started, TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS duration_sec, trx_state, trx_operation_state, trx_tables_in_use, trx_tables_locked, trx_rows_locked, trx_rows_modified FROM information_schema.INNODB_TRX ORDER BY trx_rows_modified DESC LIMIT 10; # 监控脏页生成率mysql -e SELECT 当前脏页 AS metric, VARIABLE_VALUE AS value FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_buffer_pool_pages_dirty UNION ALL SELECT 每分钟脏页生成, ROUND( (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_buffer_pool_pages_data) / (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Uptime) * 60, 0 ) UNION ALL SELECT 每分钟页面修改, ROUND( (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_pages_modified) / (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Uptime) * 60, 0 ); 4. 系统级优化内核参数优化# /etc/sysctl.conf# 虚拟内存优化vm.swappiness1# 减少 swap 使用vm.dirty_ratio10# 系统脏页比例阈值vm.dirty_background_ratio5# 后台刷新阈值vm.dirty_expire_centisecs3000# 脏页超时时间30秒vm.dirty_writeback_centisecs500# 后台刷新间隔5秒# I/O 调度和队列vm.vfs_cache_pressure1000# 回收 page cache 的倾向fs.aio-max-nr1048576# 增加异步 I/O 限制# 应用配置sysctl -pNUMA 优化如果适用# 检查 NUMA 配置numactl --hardware# 如果启用 NUMA绑定 MySQL 到特定节点# 方法1启动时绑定numactl --cpunodebind0--membind0mysqld# 方法2在配置中设置[mysqld]innodb_numa_interleaveON5. 高级优化技巧调整刷新算法-- 1. 启用激进刷新模式高写入负载时SETGLOBALinnodb_flushing_avg_loops10;-- 减少平均值计算周期SETGLOBALinnodb_adaptive_max_sleep_delay150000;-- 最大睡眠延迟-- 2. 监控并调整自适应刷新-- 查看当前的刷新压力SELECTVARIABLE_NAME,VARIABLE_VALUEAS当前值,CASEWHENVARIABLE_NAMEInnodb_buffer_pool_pages_dirtyANDVARIABLE_VALUE(SELECTVARIABLE_VALUEFROMperformance_schema.global_statusWHEREVARIABLE_NAMEInnodb_buffer_pool_pages_total)*0.7THEN建议增加 innodb_io_capacityWHENVARIABLE_NAMEInnodb_buffer_pool_wait_freeANDVARIABLE_VALUE1000THEN刷新速度跟不上ELSE正常ENDAS建议FROMperformance_schema.global_statusWHEREVARIABLE_NAMEIN(Innodb_buffer_pool_pages_dirty,Innodb_buffer_pool_wait_free,Innodb_log_waits);使用 Performance Schema 深度分析-- 分析 Page Cleaner 工作模式SELECTevent_name,count_star,sum_timer_wait/1000000000astotal_sec,avg_timer_wait/1000000000asavg_secFROMperformance_schema.events_waits_summary_global_by_event_nameWHEREevent_nameLIKE%innodb/page%cleaner%ORevent_nameLIKE%innodb/buf%flush%ORDERBYsum_timer_waitDESC;-- 查看刷新延迟分布SELECT刷新延迟分析as分析,MIN_TIMER_WAIT/1000000asmin_ms,AVG_TIMER_WAIT/1000000asavg_ms,MAX_TIMER_WAIT/1000000asmax_msFROMperformance_schema.events_waits_summary_global_by_event_nameWHEREevent_namewait/io/file/innodb/innodb_data_file;6. 紧急情况处理脏页积压应急处理#!/bin/bash# emergency-dirty-page-flush.shecho 脏页积压应急处理 # 1. 检查紧急程度DIRTY_PCT$(mysql -Nse SELECT ROUND((SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAMEInnodb_buffer_pool_pages_dirty)/(SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAMEInnodb_buffer_pool_pages_total)*100,2) 2/dev/null || echo 0) echo 当前脏页比例:${DIRTY_PCT}% if [$(echo$DIRTY_PCT 90|bc)-eq 1 ]; then echo 紧急: 脏页超过90%立即处理 # 2. 临时提高 I/O 容量 mysql -e SET GLOBAL innodb_io_capacity8000; mysql -e SET GLOBAL innodb_io_capacity_max16000; # 3. 降低脏页阈值强制刷新 mysql -e SET GLOBAL innodb_max_dirty_pages_pct50; mysql -e SET GLOBAL innodb_max_dirty_pages_pct_lwm30; # 4. 减少负载如果可能 echo 考虑: echo - 暂停批量作业 echo - 分流读流量 echo - 降低写入速率 # 5. 监控恢复 watch -n 1 echo 脏页变化: mysql -Nse SELECT VARIABLE_NAME, VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME\Innodb_buffer_pool_pages_dirty\ 2/dev/null echo -e \n磁盘 I/O: iostat -dx11|grep-A1\Device\ fi性能监控仪表板#!/bin/bash# page-cleaner-dashboard.shechoecho Page Cleaner 性能仪表板echowhiletrue;doclear# 获取数据DATA$(mysql -Nse SELECT -- 缓冲池状态 (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_buffer_pool_pages_total), (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_buffer_pool_pages_dirty), (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_buffer_pool_pages_flushed), -- I/O 统计 (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_data_writes), (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_data_fsyncs), -- 性能指标 (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_buffer_pool_wait_free), (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME Innodb_log_waits) 2/dev/null||echo0 0 0 0 0 0 0)TOTAL$(echo$DATA|awk{print $1})DIRTY$(echo$DATA|awk{print $2})FLUSHED$(echo$DATA|awk{print $3})WRITES$(echo$DATA|awk{print $4})FSYNCS$(echo$DATA|awk{print $5})WAIT_FREE$(echo$DATA|awk{print $6})LOG_WAITS$(echo$DATA|awk{print $7})# 计算比例DIRTY_PCT$((DIRTY*100/TOTAL))# 显示仪表板echo刷新时间:$(date%Y-%m-%d %H:%M:%S)echo------------------------------------------------printf缓冲池使用: %d / %d 页\n$DIRTY$TOTALprintf脏页比例: %d%%\n$DIRTY_PCTecho# 进度条显示echo-n脏页比例: [foriin$(seq150);doif[$i-le$((DIRTY_PCT/2))];thenecho-n█elseecho-n░fidoneecho]echo# 刷新统计echo刷新统计:printf 累计刷新: %d 页\n$FLUSHEDprintf 数据写入: %d 次\n$WRITESprintf Fsync调用: %d 次\n$FSYNCSecho# 等待统计echo等待统计越低越好:printf 缓冲池等待空闲: %d\n$WAIT_FREEprintf 日志等待: %d\n$LOG_WAITSecho# 建议echo建议:if[$DIRTY_PCT-gt80];thenecho 脏页过多考虑增加 innodb_io_capacityelif[$WAIT_FREE-gt100];thenecho ⚠️ 刷新跟不上检查磁盘性能elif[$LOG_WAITS-gt10];thenecho ⚠️ 日志文件可能太小elseecho ✅ 状态良好fiecho------------------------------------------------echo按 CtrlC 退出监控sleep2done总结关键要点脏页是正常现象- 是数据库性能优化的一部分Page Cleaner 是多线程的- 合理配置线程数很重要刷新效率取决于多方面- MySQL配置、磁盘性能、应用模式监控是关键- 使用提供的脚本持续监控提高刷新效率的黄金法则-- 配置优化优先级1.磁盘性能最快的可用存储2.innodb_io_capacity匹配磁盘性能3.innodb_page_cleaners匹配CPU核心数4.innodb_buffer_pool_size足够大5.innodb_log_file_size减少检查点-- 应用优化1.合理事务大小2.批量操作3.避免热点更新记住优化是一个持续的过程需要根据实际负载不断调整和监控。