innodb通过buf_page_get_gen()函数实现数据页加载:先查buffer pool,未命中则调fil_io()异步读取16kb整页;满时按lru淘汰,young/old分区防污染;物理读才是性能瓶颈,高命中率掩盖i/o压力;预读分线性和随机两种,线性需连续13页顺序访问才触发;checksum校验失败直接报错中止查询。

MySQL 读数据页时,InnoDB 怎么从磁盘加载到 buffer pool
InnoDB 不是每次查记录都直接读磁盘,而是先查 buffer pool;没命中才触发「物理读」——即从磁盘读取一个完整的 16KB 数据页(page)进内存。
这个过程由 buf_page_get_gen() 函数驱动,它会按顺序检查:是否已在 buffer pool 中 → 是否在 LRU list 里 → 若不在,就调用 fil_io() 发起异步 I/O 读取对应 space_id 和 page_no 的页。
- 页加载是「整页加载」,哪怕只查一行,也要把整个 16KB 页拉进来
- 如果
buffer pool已满,会按 LRU 策略淘汰冷页,但有young_list/old_list分区避免全表扫描污染热数据 -
innodb_old_blocks_pct默认 37,影响新页进入 old 区的比例,调太低会导致预读页过早被踢出
为什么 SELECT 有时慢得离谱,但 SHOW ENGINE INNODB STATUS 却显示 buffer pool 命中率 99%
高命中率只说明「逻辑读」多,掩盖了底层的物理读压力。真正卡顿往往发生在第一次访问某页时的磁盘 I/O,尤其是随机 I/O 场景下,机械盘寻道延迟明显。
常见诱因:ORDER BY 配合无索引字段、LIMIT 跳过大量偏移、或 JOIN 导致非顺序访问多个分散页。
- 用
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads'看真实物理读次数,比命中率更有参考价值 -
Innodb_data_reads是操作系统层面的 read() 调用次数,含预读页,通常大于前者 - SSD 可缓解但不消除问题——页定位、解析、校验(
innodb_checksum_algorithm)仍需 CPU 时间
预读(read-ahead)什么时候触发?怎么判断是不是它在捣鬼
InnoDB 在检测到连续访问模式时会主动预读后续页,分两种:线性预读(linear read-ahead)和随机预读(random read-ahead),后者已从 5.7+ 默认禁用。
线性预读条件苛刻:当前页所在 extent 内,连续 13 个页被访问过,且都是顺序请求,才会触发读取剩余 32 页。所以不是“一读就预读”,更不是“所有范围查询都预读”。
- 可通过
SHOW ENGINE INNODB STATUS中的Pages read ahead字段确认是否发生 - 误判常出现在大批量
SELECT ... LIMIT 10000,10场景——看似顺序,实则跳过大量页,触发无效预读并挤占 buffer pool - 关闭线性预读:设置
innodb_read_ahead_threshold=0,但别在 OLTP 核心库轻易动,可能加重单页 I/O
page checksum 失败后,InnoDB 怎么处理损坏页
页加载进 buffer pool 后,InnoDB 会立即校验 checksum(默认 crc32)。校验失败不会静默忽略,而是抛出错误并中止查询,日志里出现 Database page corruption 或 invalid page in log。
注意:这发生在「页加载后、解析前」,所以即使 SQL 语法完全正确,也可能因底层页损坏而报错。
- 若
innodb_force_recovery > 0,部分校验会被跳过,但风险极高,仅用于紧急导出 -
innodb_checksum_algorithm=strict_crc32比默认值更严格,能提前暴露存储层写异常 - 云上环境要特别留意:某些虚拟块设备或快照链路可能静默损坏页尾部,导致 checksum 不匹配
页级加载和校验是 InnoDB 最底层的数据可信边界,任何绕过它的“修复”操作(比如直接 dd 替换页)都会让后续事务状态不可预测。











