这是《MySQL 8.0 源码与内核实战》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 Buffer Pool 是 InnoDB 的内存页缓存。脏页、干净页、索引页、undo 页、锁信息和 AHI 都与其生命周期相关。
16.1 基本结构
logical page request
-> page hash lookup
|-- hit: return block
+-- miss:
-> read page from disk
-> insert into LRU
-> return block
关键点:
- 以页为单位缓存;
- 查找通过 page hash;
- 淘汰通过 LRU 变体;
- 修改页成为脏页;
- 脏页由 page cleaner 刷盘;
- 页 latch 控制并发访问。
16.2 LRU 变体
InnoDB 使用 young/old 子链表,降低全表扫描污染热数据的风险。
new pages
-> old sublist
-> survive threshold time
-> move to young sublist
相关参数:
| 参数 | 说明 |
|---|---|
innodb_buffer_pool_size |
总大小 |
innodb_buffer_pool_instances |
实例数 |
innodb_old_blocks_pct |
old 区比例 |
innodb_old_blocks_time |
进入 young 的停留时间 |
16.3 脏页刷盘
modify page in memory
-> mark dirty
-> write redo first
-> page cleaner flush
-> doublewrite
-> data file
触发条件:
- redo 空间压力;
- 脏页比例;
- LRU 空间不足;
- checkpoint;
- shutdown;
- 用户线程辅助。
相关参数:
| 参数 | 说明 |
|---|---|
innodb_max_dirty_pages_pct |
脏页比例目标 |
innodb_io_capacity |
正常刷盘能力 |
innodb_io_capacity_max |
压力上限 |
innodb_lru_scan_depth |
LRU 扫描深度 |
16.4 命中率
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';
常用计算:
hit rate = 1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests
| 现象 | 方向 |
|---|---|
| 命中率低 | 内存不足、随机 IO、冷数据、大扫描 |
| 命中率高但慢 | CPU、锁、日志、网络 |
| 脏页高 | 刷盘能力不足 |
| free wait | LRU 空间紧张 |
16.5 并发控制
Buffer Pool 并发涉及:
- buffer pool instance mutex;
- page latch;
- LRU list mutex;
- flush list mutex;
- free list mutex;
- AHI latch。
性能视图:
SELECT event_name, count_star, sum_timer_wait
FROM performance_schema.events_waits_summary_global_by_event_name
WHERE event_name LIKE 'wait/synch/mutex/innodb/%'
ORDER BY sum_timer_wait DESC;
16.6 在线调整
SET GLOBAL innodb_buffer_pool_size=8589934592;
在线调整可能引发内存分配和内部锁等待,应在低峰执行并监控状态:
SHOW STATUS LIKE 'Innodb_buffer_pool_resize_status';
生产建议提前规划内存,不把它当常规动态调优手段。
16.7 页面观测
SELECT SPACE, PAGE_TYPE, COUNT(*) AS pages
FROM information_schema.innodb_buffer_page
GROUP BY SPACE, PAGE_TYPE
ORDER BY pages DESC
LIMIT 20;
查询系统表可能产生额外开销,生产上应限流或在测试库分析。
16.8 源码入口
| 文件 | 内容 |
|---|---|
buf0buf.cc |
Buffer Pool 核心 |
buf0lru.cc |
LRU 和淘汰 |
buf0flu.cc |
刷脏 |
buf0rea.cc |
预读 |
buf0dblwr.cc |
doublewrite |
断点:
b buf_page_get_gen
b buf_LRU_get_free_block
b buf_flush_page
本章小结
Buffer Pool 通过 LRU 变体缓存页,通过脏页列表和 page cleaner 异步落盘。命中率和脏页趋势是内存与 IO 判断的核心指标,在线调大需谨慎。
思考题
- 为什么 InnoDB LRU 分 young/old 区?
- redo log 压力如何影响脏页刷盘?
- 命中率低有哪些原因?
- dirty pages 高说明什么?
- 在线调整 Buffer Pool 应注意什么?