mysqldump --quick 仍会 OOM 是因它仅缓解客户端内存压力,无法解决服务端 innodb_buffer_pool 不足、tmpfs 空间耗尽、LONGBLOB 行镜像构建、元数据加载(如 --routines)及 MyISAM 锁表等根本问题。

直接结论:别让 mysqldump 一次性拉取含 LONGBLOB 的整表数据,它会把服务端和客户端同时拖垮;真正有效的做法是绕过客户端缓冲、分段导出、显式控制内存边界,并在应用层提前剥离大字段。
为什么 mysqldump --quick 仍会 OOM
--quick 只解决客户端内存缓存问题,对服务端无影响。当表里有几十 MB 的 LONGBLOB 字段时:
- 服务端仍需在
innodb_buffer_pool中构建完整行镜像,innodb_buffer_pool_size不足就会触发 swap 或被 OOM Killer 杀掉 -
/tmp是 tmpfs(内存挂载)时,mysqldump中间落盘失败会报 “Cannot allocate memory”,实为磁盘空间耗尽 - 若用了
--routines或导出视图,元数据加载不走流式逻辑,照样堆满sp_head::main_mem_root - MyISAM 表锁住期间无法流式输出,
--quick完全失效
用 SELECT INTO OUTFILE 绕过客户端瓶颈
这是最稳的导出路径——由 MySQL 服务端直写磁盘,不经过客户端内存缓冲:
- 先查
SHOW VARIABLES LIKE 'secure_file_priv',导出路径必须落在该目录下,如/var/lib/mysql-files/ - 语句必须显式指定字段,避免换行符破坏 CSV 格式:
SELECT id, file_path FROM images INTO OUTFILE '/var/lib/mysql-files/images.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n'; - 不支持
JOIN或多表,适合单表分段导出(加WHERE id BETWEEN 1 AND 10000) - 导出后文件权限属
mysql用户,你本机无法直接读,需先scp或rsync拉回
导入 LOAD DATA INFILE 前必须调的三个参数
导出成功不等于能导入,LOAD DATA INFILE 对服务端和客户端都有硬性要求:
- 服务端
max_allowed_packet必须 ≥ 单行最大长度(例如含 20MB Base64 字段的行),建议设为512M并重启mysqld - 客户端连接也得同步调大:
mysql --max-allowed-packet=512M -u root db_name,否则 MySQL 客户端库会在传输层截断 - 字符集必须显式声明:
LOAD DATA INFILE '/var/lib/mysql-files/images.csv' CHARACTER SET utf8mb4,否则高发Incorrect string value - 导入前执行:
SET FOREIGN_KEY_CHECKS=0; SET UNIQUE_CHECKS=0; SET sql_log_bin=0;,避免逐行校验拖慢速度
更彻底的解法:应用层提前拆分 BLOB 存储
迁移只是临时手段,长期来看,把 BLOB 从主表移出去才是根治方式:
- 新建外置表,如
user_avatars,只存file_path、file_size、mime_type等元数据 - 应用层上传文件到对象存储或本地路径后,再插入外置表,并用事务更新主表的
avatar_id字段 - 原主表中删掉
avatar_blob字段,既提升查询性能,又消除所有溢出页、临时表、内存爆满风险 - 注意:不要用
VARCHAR(65532)替代BLOB,InnoDB 会自动转成TEXT并警告,且仍触发溢出页
真正容易被忽略的是:溢出页本身不报错,但每次 SELECT * 都会额外触发一次随机 I/O 去加载它;而这个开销,在你只看 EXPLAIN 和 Rows_examined 时根本看不到。


















