“sending data”状态耗时长主因是回表读取大字段(如varchar(8000))引发的溢出页随机i/o,而非网络发送慢;该状态实际涵盖“收集数据(回表)+发送数据”,其中收集占绝大部分,可通过show profile、handler_read_rnd指标及字段对比验证。

“Sending data”不是网络卡,而是MySQL正在拼命读磁盘——多数时候它根本还没开始发数据。
为什么SHOW PROCESSLIST看到Sending data却查不出慢查询?
因为这个状态覆盖了执行器里最耗时的两个动作:从聚簇索引回表取字段 + 把结果组装发送给客户端。而真正卡住的,90%以上是前者。
- 慢查询日志默认只记录执行时间超过
long_query_time的语句,但“Sending data”阶段的耗时可能分散在多次I/O中,单次不超阈值,日志就漏掉了 -
EXPLAIN只能看到是否走索引、是否回表,但看不出大字段(如VARCHAR(8000))引发的溢出页随机读有多惨烈 - 如果被锁住(比如另一事务持
X锁未提交),SHOW PROCESSLIST照样显示Sending data,实际是等锁,不是真在读
怎么确认是不是回表读大字段导致的?
用SHOW PROFILE FOR QUERY N看各阶段耗时,重点盯Copying to tmp table和Sending data这两项占比。如果后者占总耗时70%以上,且Handler_read_rnd或Handler_read_rnd_next计数飙升,基本就是回表+大字段双重打击。
- 检查
SELECT字段:有没有TEXT、VARCHAR(5000+)、BLOB这类字段参与返回?哪怕只查200行,单行溢出页读取可能触发3~5次随机I/O - 对比
SELECT *和SELECT id, name的执行时间——如果后者快10倍,问题就坐实了 - InnoDB的
ROW_FORMAT=COMPACT下,超过768字节的字段内容会存到独立溢出页,这些页通常不在innodb_buffer_pool里缓存
为什么调大innodb_buffer_pool_size有时没用?
因为溢出页(overflow page)默认不进Buffer Pool——这是InnoDB故意设计的,避免大字段查询把热数据全挤出去。所以光堆内存,解决不了溢出页反复读盘的问题。
-
innodb_buffer_pool_size调再大,对VARCHAR(8000)字段的溢出页也基本无效 - 真正有效的做法是:要么删掉不必要的大字段返回,要么建覆盖索引(把所有
SELECT字段都包含进索引),让MySQL不用回表 - 拆表也是选项,比如把
description单独拎到article_content表,主表只留id和summary,查详情时再JOIN——但要注意JOIN本身带来的锁和延迟成本
最常被忽略的一点:Sending data状态在MySQL 8.0.17+已合并进Executing,但它掩盖的底层问题没变——回表、锁等待、溢出页读取,依然在原地等着你去定位。











