根本原因是客户端程序将整表结果一次性加载至内存,解决方法是启用流式读取:mysqldump加--quick参数;Python使用SSCursor+fetchone;JDBC设置TYPE_FORWARD_ONLY、CONCUR_READ_ONLY及setFetchSize(Integer.MIN_VALUE)。

不是MySQL服务端崩了,是你的客户端程序(比如 mysqldump、Python脚本、Java应用)把整张表结果全塞进内存才崩的。
mysqldump 导出大表时内存暴涨
默认情况下,mysqldump 会启用 mysql_store_result 模式:它先把整张表所有行一次性拉到本地内存,再拼成 SQL 写出去。1000 万行 × 平均每行 2KB,光数据就 20GB —— 还没算字符串开销和 Python/Java 对象头。
- 必须加
--quick:强制切换为mysql_use_result流式读取,边查边写,内存恒定在几 MB - 顺手加
--skip-extended-insert:避免单条 INSERT 超过max_allowed_packet(哪怕你设了 256M,一条语句也可能超) - 别碰
--lock-tables:它会锁表 + 缓存全部数据,双重加压;用--single-transaction(InnoDB)更安全
Python pymysql / mysqlclient 查询时爆内存
调 cursor.execute("SELECT * FROM huge_table") 后,默认缓存全部结果。哪怕你只想要 CSV,Python 进程也先扛下全部数据。
- pymysql:创建连接时指定
cursorclass=pymysql.cursors.SSCursor,或执行前调cursor.unbuffered() - mysqlclient:用
cursorclass=MySQLdb.cursors.SSCursor - 绝对别用
fetchall();改用fetchone()或fetchmany(size=1000)控制批次 -
max_allowed_packet是服务端参数,只管单包大小,跟流式导出内存无关——调大它解决不了客户端 OOM
JDBC 应用查询大结果集卡死或 OOM
JDBC 默认把整个 ResultSet 加载进 JVM 堆,不等你调 next() 就已分配完内存。
- 方案一:用
PreparedStatement设置游标类型和 fetch size:conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY),再setFetchSize(Integer.MIN_VALUE) - 方案二:强转为 MySQL 特有接口:
((com.mysql.cj.jdbc.StatementImpl) stmt).enableStreamingResults() - 注意:流式模式下不能调
rs.last()或rs.getRow(),否则会触发缓冲回填
真正容易被忽略的是:服务端 innodb_buffer_pool_size 再大,也救不了客户端缓存行为;OOM 的锅永远在客户端驱动层是否流式,而不是“数据太大”。


















