Yihui’s Blog

MySQL 中 SELECT 查询 1000 万行,内存会飙升吗?

日期:2026-07-11
标签:#面试 #八股 #后端 #MySQL #数据库 #场景题

一句话答案

很可能造成数据库、网络和客户端内存及资源压力,但是否一次性飙升取决于执行计划、排序临时表、驱动缓冲方式和应用是否流式消费;无论如何都不应无界查询千万行。

面试口语版

MySQL 执行全表扫描时 InnoDB 主要通过 Buffer Pool 读页,不一定把 1000 万结果全部同时放进一块结果内存,但排序、分组或临时表可能消耗大量内存并落盘。更常见的风险在客户端:很多 JDBC 驱动默认缓存完整 ResultSet,应用堆会快速增长甚至 OOM,同时长连接、长事务、网络传输和 GC 都受影响。正确方式是只查必要列、按主键游标分页或使用真正的流式读取,批量处理并及时释放;大导出放异步任务并限速。

影响层次

层次可能问题
MySQL全表扫描、Buffer Pool 污染、临时表、长事务
网络巨量结果传输、带宽占满、超时
JDBCResultSet 全量缓存或预取过大
JVM对象膨胀、GC、OOM

关键细节

  • SELECT * 增加行宽、网络和对象创建,还可能破坏覆盖索引。
  • JDBC fetchSize 语义依驱动与版本配置而异,必须查文档并实测。
  • 流式读取期间连接被长期占用,应设置超时并隔离任务。
  • 读副本能隔离部分压力,但不能消除带宽和客户端风险。

面试官追问

  1. MySQL 会一次把全部结果放内存吗?
  2. JDBC 如何实现流式读取?
  3. 为什么长时间流式查询也有风险?

面试官追问参考答案

1. MySQL 会一次把全部结果放内存吗?

普通扫描通常边读边发送,数据页进入 Buffer Pool,不等于构建一个包含全部行的内存数组;但排序、聚合、临时表和网络缓冲会额外占内存,内存不足后可能落盘。客户端行为往往决定是否全量缓存结果。

2. JDBC 如何实现流式读取?

使用预编译语句、只读前向 ResultSet,并按具体 MySQL Connector/J 版本配置 cursor fetch/fetchSize 或流式模式,逐批处理后释放对象。配置语义随驱动版本变化,必须用真实环境验证堆占用和网络行为。

3. 为什么长时间流式查询也有风险?

它会长期占用数据库连接和网络,若处于一致性事务中还可能保留旧版本、拖长 undo 清理;慢消费者会形成背压。应按主键分批提交、设置任务超时和限速,必要时从快照或分析库导出。

学习清单

  • 区分数据库、驱动和 JVM 三层内存。
  • 掌握游标分页与流式读取的适用场景。
维护与整理 · Yihui在 GitHub 上编辑

继续阅读

浏览全部文章