根本原因是mysqldump客户端默认启用mysql_store_result模式,将整表结果集全载入内存;须加--quick启用流式读取、--skip-extended-insert避免长语句,并禁用--lock-tables。

不是数据太大,是客户端默认把整张表结果全塞进内存里了。
mysqldump 备份大字段表时 OOM 的真实原因
根本不在服务端,而在 mysqldump 客户端——它默认启用 mysql_store_result 模式,会把 SELECT * 的全部结果(包括所有 TEXT、BLOB 字段)一次性加载到本地内存再写文件。哪怕你只导出 100 万行、每行 content 字段平均 2MB,内存瞬间就吃掉 200GB+。
这种行为和 max_allowed_packet 无关,调大这个参数只会让单次网络包更大,但不会改变“全量缓存”的本质。
-
--quick必须加:强制切换为mysql_use_result流式读取,边查边写,内存占用稳定在几 MB 级别 -
--skip-extended-insert必须加:避免单条 INSERT 包含数万行数据,否则可能触发max_allowed_packet或解析崩溃 -
--single-transaction可选但推荐(InnoDB 表):配合--quick使用,不锁表;但千万别加--lock-tables,它会让备份期间阻塞写入且加剧内存压力
Python 脚本导出大字段表也一样会崩
pymysql / mysqlclient 默认也是全量缓存。执行 cursor.execute("SELECT * FROM huge_table") 后,不手动干预,整个结果集就已躺在 Python 进程内存里了。
- pymysql:创建连接时指定
cursorclass=pymysql.cursors.SSCursor,或执行前调用cursor.unbuffered() - mysqlclient:用
cursorclass=MySQLdb.cursors.SSCursor - 绝对不要用
fetchall();改用fetchone()或fetchmany(size=1000)控制每次读取量 - 如果还报
PacketTooLarge,说明服务端max_allowed_packet不够——但这只影响单条语句或单个字段长度,和流式导出无关;设为256M即可(需服务端 SET 或重启)
为什么只看 innodb_buffer_pool_size 会误判?
很多人一看到 OOM 就去调小 innodb_buffer_pool_size,但这次真凶压根不在服务端。真正该盯的是:
- mysqldump 进程的 RSS 内存(
ps aux --sort=-%mem | head -5),确认是不是它在吃光内存 - 导出脚本里有没有隐式缓存(比如 pandas.read_sql 直接 load 全表)
- 是否用了 ORM(如 SQLAlchemy)的默认
session.execute().fetchall(),而不是流式迭代器
流式读取不是“慢一点”,而是唯一能防止 OOM 的路径;一旦漏掉 --quick 或没关 cursor buffer,再多内存也扛不住。











