mysql客户端在服务端返回第一行数据后立即接收,因服务端边查边发:存储引擎返回首行即序列化为row data packet发送,客户端调用fetch方法即可获取;但order by无索引、group by等操作会阻塞首行返回。

MySQL 客户端如何收到第一条 SELECT 结果行?
MySQL 不是等整个结果集生成完才发给客户端,而是边查边发。服务端执行 SELECT 时,一旦存储引擎返回第一行数据(比如从 InnoDB 的 B+ 树中读出一条记录),就立即序列化为 MySQL 协议的 Row Data Packet,通过 socket 写入 TCP 发送缓冲区——此时客户端调用 mysql_fetch_row() 或 cursor.fetchone() 就可能拿到它。
这意味着:
- 如果查询扫描千万行但客户端只取前 10 行,服务端可能在发完第 10 行后就停止读取(前提是没加
SQL_NO_CACHE且优化器能做 early termination) - 但如果用了
ORDER BY+LIMIT且排序字段无索引,服务端仍需先排完全部结果,再截前 N 行——这时「边查边发」失效,客户端要等到排序完成才收到首行 -
GROUP BY、窗口函数、临时表等操作也会阻塞首行返回,因为必须攒够一批数据才能计算
net_buffer_length 和 max_allowed_packet 怎么影响分包?
MySQL 不把整行或整结果集打成一个包发出去。它用 net_buffer_length(默认 16KB)作为内存缓冲区上限:只要当前行序列化后不超过该值,就攒在 buffer 里;超了就立刻 flush 成一个 packet。而单个 packet 绝不能超过 max_allowed_packet(默认 64MB),否则报错 Got a packet bigger than 'max_allowed_packet' bytes。
典型影响场景:
- 查一个含
TEXT字段的表,某行实际数据 20KB → 因为超net_buffer_length,这行会单独发一个 packet;下一行哪怕只有 100 字节,也得等 buffer 再次填满或查询结束才发 - 批量插入多行时,如果拼成一条
INSERT ... VALUES (...), (...), (...),整条语句文本长度超max_allowed_packet,连接直接断开,报错Packets larger than max_allowed_packet are not allowed - 调整建议:对大字段查询,可适当调高
net_buffer_length减少小包数量,但别设太高,否则内存浪费且延迟感知变差
客户端 fetch 时,服务端还在继续发数据吗?
是的,只要连接没关、服务端没遇到错误或被 kill,它会持续将后续行打包发送。但关键在于:TCP 是流式协议,没有「消息边界」——客户端收到的字节流是连续的,MySQL C API 或 Python mysqlclient 这类驱动靠解析 packet header(前 3 字节长度 + 1 字节 sequence ID)来拆包。
常见陷阱:
- 自己写 socket 读取时,如果只调一次
recv(4096),很可能只读到半个 packet,导致后续所有解析错位。必须按 header 指定长度循环读满 - 使用异步驱动(如
aiomysql)时,如果 awaitfetchone()后不继续调用fetchone()或fetchall(),未读完的 packet 会留在 socket 接收缓冲区,下次执行新 query 时可能混入上一结果的残留数据,触发Commands out of sync - 客户端调用
mysql_free_result()或关闭 cursor,服务端收到 FIN 包后才会真正停止发送;在此之前,它可能已把剩余几万行全推到 TCP 发送队列里了
为什么 SHOW PROCESSLIST 里状态常卡在 Sending data?
这个状态名有误导性——它不单指「正在往网卡发数据」,而是涵盖「从引擎读数据、计算、序列化、写 socket」整个后端执行阶段。即使网络空闲,只要服务端还在处理行(比如执行 CONCAT()、JSON_EXTRACT() 或用户自定义函数),状态就维持在此。
排查重点:
- 看
Info列具体 SQL,确认是否有复杂表达式或子查询嵌套 - 用
EXPLAIN FORMAT=TREE检查是否出现Using temporary; Using filesort,这类操作必然拉长该状态时间 - 对比
Rows_examined和Rows_sent:如果前者远大于后者(如扫描 100 万行只返回 100 行),说明过滤逻辑低效,瓶颈在存储引擎层而非网络
真正的网络传输耗时通常极短,除非客户端带宽极低或服务端 net_write_timeout 设置过小导致频繁重传。多数情况下,盯着 Sending data 本质是在盯 SQL 执行效率本身。










