MySQL 查询执行完后,数据怎么传给客户端?
MySQL 服务端不会“主动推送”结果集,而是等客户端持续调用
mysql_fetch_row()(C API)、
cursor.fetchone()(Python MySQLdb/PyMySQL)或等价的驱动方法,才逐批从网络缓冲区取数据。这个过程本质是「服务端写入 socket、客户端读取 socket」,中间受
net_buffer_length和
max_allowed_packet控制。
net_buffer_length决定服务端内部暂存单行数据的初始缓冲区大小(默认 16KB),不够时会自动扩容,但频繁扩容影响性能
max_allowed_packet是单次网络包上限(默认 4MB),超限会导致
Packet too large错误,且该值必须 ≥ 客户端与服务端两端配置 如果客户端不及时读取(比如 Python 中用
fetchall()但结果集超大),服务端会在发送完所有数据前就关闭连接,报错
MySQL server has gone away
为什么 LIMIT 1000 还卡住?可能卡在传输阶段
执行
SELECT * FROM huge_table LIMIT 1000很快,但客户端拿到第一行却要等几秒——这往往不是查询慢,而是服务端正在把 1000 行数据打包发往网络层。尤其当字段含
TEXT或
BLOB时,单行体积暴涨,触发多次
max_allowed_packet分片和 socket write 阻塞。 检查实际返回字节数:
SELECT SUM(LENGTH(col1) + LENGTH(col2) + ...) FROM huge_table LIMIT 1000;用
tcpdump或
Wireshark抓包看是否出现大量小包( 临时缓解:客户端加
use_unicode=False(Python PyMySQL)避免 UTF-8 编码开销;服务端调高
net_buffer_length到 64K~256K
mysql_real_query() 后没调 mysql_store_result(),会发生什么?
C API 下,调用
mysql_real_query()只是提交 SQL,真正拉取结果必须显式调
mysql_store_result()(缓存全部结果到内存)或
mysql_use_result()(流式读取)。漏掉这步,后续任何操作(包括下一条
mysql_real_query())都会失败,报错
Commands out of sync; you can't run this command now。
mysql_store_result():适合小结果集,内存占用高但后续访问快
mysql_use_result():适合大结果集,内存低但要求客户端严格按顺序读完,期间不能发新 query Python 的
cursor.execute()默认等价于
mysql_store_result();若想流式读,需设
cursor = conn.cursor(buffered=False)
客户端断连时,MySQL 服务端知道吗?
服务端通常**不知道**客户端已断开,除非下一次尝试写 socket 时收到
EPIPE或
SIGPIPE。这意味着:一个长期未读取结果的查询,会一直占着连接、线程和临时内存,直到超时(
wait_timeout或
interactive_timeout)被 kill。 现象:
SHOW PROCESSLIST中看到状态为
Sending data且
Time持续增长,但客户端早已关闭 验证方式:在客户端执行
kill -9进程后,服务端该连接仍显示
Sleep或
Sending data数分钟 关键防御:服务端设合理的
wait_timeout(如 60 秒),客户端代码确保每个
execute()后必有
fetch*或
close()
客户端最容易忽略的是「查询结束 ≠ 数据送达」——只要还有一行没被客户端明确取走,服务端就认为这次交互尚未完成。
