网络带宽被“悄悄吃掉”是因为SELECT *强制传输所有字段字节数,而非仅行数;尤其含TEXT/BLOB时,页外存储导致多次IO与数据拼装,使单行体积激增,10万行多选2KB字段即增200MB负载。

SELECT列过多直接导致传输数据量膨胀,尤其当表中存在大字段(如 TEXT、BLOB、长 VARCHAR)时,带宽消耗会呈非线性增长。
为什么网络带宽会被“悄悄吃掉”
数据库返回结果不是按“行数”计费,而是按“字节数”传输。哪怕你只多选一个 log_content 字段(平均长度 2KB),10 万行就额外增加约 200MB 网络负载。MySQL 协议基于 TCP,即使 DB 和应用同机部署,也要走 socket 栈和内核缓冲区,这部分开销不可忽略。
常见错误现象:
- 应用端内存暴涨或 OOM,但数据库 CPU/IO 并不高
- 查询耗时稳定在 200–500ms,但
netstat显示大量ESTABLISHED连接持续发送数据 - 监控发现
Bytes_sent指标远高于业务预期(比如单次查询发了 80MB)
大字段(BLOB/TEXT)会让带宽问题更严重
InnoDB 对超过 728 字节的字段会启用“页外存储”:主记录只存指针,真实数据另存别处。SELECT 这类字段时,MySQL 必须额外读取分散的页块,再拼装后发给客户端——这不仅加重磁盘 I/O,还放大网络包数量和总大小。
使用场景中容易踩坑的点:
- 用户头像存为
BLOB,但接口只要id和nickname,却写了SELECT * - 日志表含
full_stack_trace TEXT,排查时习惯性SELECT * FROM logs WHERE id = 123 - ORM 默认生成
SELECT *(如 Django 的Model.objects.get()在未指定values()时)
LIMIT 不能解决列过多带来的带宽问题
LIMIT 100 只限制行数,不压缩每行体积。如果每行因 SELECT * 多带 50KB,100 行仍是 5MB;而明确写 SELECT id, name, updated_at 可能压到每行 100 字节以内,总量不到 10KB。
参数差异要注意:
-
SELECT *+LIMIT 100:仍会读取并传输所有列,只是截断行数 -
SELECT id, name+LIMIT 100:数据库可利用覆盖索引跳过聚簇索引回表,且只序列化两个字段 - 某些代理层(如 ProxySQL)对
SELECT *的缓存效率更低,因字段列表不可预测
真正影响带宽的是你让数据库“吐出来什么”,而不是“吐多少行”。哪怕只查 1 行,带上 icon_md5 和 raw_config JSON 也可能发几 MB 数据——这点在微服务间调用或移动端 API 中尤其致命。

















