物理读是innodb从磁盘加载数据页(16kb)到buffer pool的次数,对应innodb_buffer_pool_reads;逻辑读是sql向buffer pool发起的数据页访问总次数,对应innodb_buffer_pool_read_requests,且逻辑读≥物理读。

物理读和逻辑读在MySQL里到底指什么
物理读是InnoDB真正从磁盘加载数据页(16KB)进innodb_buffer_pool的次数,对应指标是Innodb_buffer_pool_reads;逻辑读是SQL执行过程中向Buffer Pool发起的数据页访问总次数,对应Innodb_buffer_pool_read_requests。两者不是“一次物理读对应一次逻辑读”,而是:逻辑读 ≥ 物理读,差值部分来自Buffer Pool中已缓存页的重复访问。
用global_status差值法算单条SQL的物理/逻辑读数
这是最轻量、兼容性最强的方法,适用于所有MySQL 5.7+版本(包括已禁用performance_schema的生产环境):
- 先记录初始值:
SELECT variable_value INTO @init_reads FROM performance_schema.global_status WHERE variable_name = 'Innodb_buffer_pool_reads'; - 再记录逻辑读初始值:
SELECT variable_value INTO @init_reqs FROM performance_schema.global_status WHERE variable_name = 'Innodb_buffer_pool_read_requests'; - 执行目标SQL(确保它在独立连接中运行,避免其他会话干扰计数)
- 查执行后值:
SELECT variable_value INTO @final_reads FROM performance_schema.global_status WHERE variable_name = 'Innodb_buffer_pool_reads';和同理查Innodb_buffer_pool_read_requests - 计算:
SELECT (@final_reads - @init_reads) AS physical_reads, (@final_reqs - @init_reqs) AS logical_reads;
注意:该方法不能区分“哪次读来自哪个表或索引”,只反映整条SQL引发的全局Buffer Pool访问变化;若SQL执行期间有其他线程刷脏页或做后台IO,结果会有微小偏差。
为什么不能直接用Handler_read_*或SHOW PROFILE
Handler_read_first、Handler_read_key等状态变量统计的是存储引擎接口调用次数,与是否走磁盘无关——哪怕全命中Buffer Pool,它们也会涨;而SHOW PROFILE BLOCK IO在MySQL 5.7中已被标记为deprecated,其Block_gets仅反映read()系统调用次数,并非真实磁盘页读取数(page cache命中时也会计数)。你看到Block_gets = 42,实际物理读可能是0。
比例本身不重要,关键看绝对值和命中率
算出physical_reads / logical_reads这个比值意义有限。更关键的是两个绝对数字:
– 如果logical_reads高达几万但physical_reads为0,说明SQL访问模式局部性好,Buffer Pool足够;
– 如果physical_reads每秒几十次且logical_reads很小,大概率是innodb_buffer_pool_size设得太小,每次只读一页就淘汰,反复拉页;
– 真正要盯的是Innodb_buffer_pool_read_requests和Innodb_buffer_pool_reads的长期趋势,用(1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests)算出命中率,低于95%就要警惕。











