应采用流式读取避免OOM:Python用unbuffered/stream、Go用QueryContext+Next、Java设FetchSize;配合索引优化、分页导出、CSV转义、连接池限并发及避开业务高峰。

MySQL 大表导出卡死或 OOM 怎么办
直接 SELECT * FROM huge_table 再拼 CSV,内存会爆、连接会超时、服务器负载飙升。根本原因是 MySQL 默认启用了缓冲结果集(mysql_store_result),整张表数据全塞进客户端内存里。
必须切到流式读取模式,让数据边查边吐,不攒、不缓存。
- Python 用
cursor.unbuffered = True(PyMySQL)或stream=True(mysql-connector-python) - Go 用
rows, err := db.QueryContext(ctx, query)配合rows.Next()循环,不调rows.Scan全量 - Java 的
Statement.setFetchSize(Integer.MIN_VALUE)强制流式(需驱动支持,如 MySQL Connector/J 8.0+) - 命令行工具别用
mysqldump --single-transaction导大表——它仍会把单个事务结果全读进内存;改用mysql -N -s -e "SELECT ..."+ 管道分块处理
WHERE 条件没走索引导致全表扫描
流式读取救不了慢查询。如果 SELECT 没加有效过滤条件,或者 WHERE 字段没索引,MySQL 还是得扫完整张表,I/O 和锁等待时间一点没少。
导出前务必确认执行计划:EXPLAIN SELECT ... 中的 type 是 range 或更优,key 显示用了哪个索引。
- 按主键/自增 ID 分页导出最稳:
WHERE id BETWEEN ? AND ?,配合ORDER BY id - 避免
OFFSET:大偏移下LIMIT 100000, 1000依然要扫前 10 万行 - 时间范围导出注意时区和类型匹配:字段是
DATETIME就别传字符串'2024-01-01'让 MySQL 隐式转换
字符集与 NULL 值导致 CSV 格式错乱
流式导出常被忽略的细节:字段含换行符、逗号、双引号,或 NULL 被写成空字符串,下游解析直接失败。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
MySQL 默认不转义,CSV 解析器又不统一,必须在应用层做预处理。
- 用
IFNULL(col, '\N')或COALESCE(col, '')显式控制NULL输出 - 字符串字段用
REPLACE(REPLACE(col, ' ', '\n'), '"', '""')做基础转义(若目标格式要求双引号包围) - 连接初始化时执行
SET NAMES utf8mb4,确保cursor返回的 bytes 是 UTF-8,避免乱码 - 别依赖
mysqldump --fields-escaped-by='\':它只对部分特殊字符转义,且不处理字段内换行
并发导出多个分片时连接池打满或锁表
分片导出看似能提速,但若每个分片都开新连接、共用同一账号,容易触发 max_connections 限制,或在 READ COMMITTED 下因 MVCC 版本链过长拖慢其他业务。
更关键的是:InnoDB 表级锁虽少,但大查询仍可能持有 dict_operation_lock 或引发 buffer pool 争用。
- 严格限制并发数:通常 2–4 个分片足够,再多反而因 I/O 争抢下降
- 用
SELECT ... FOR SHARE替代默认一致性读,减少 undo log 压力(尤其在高更新频率表上) - 避开业务高峰,导出前检查
SHOW PROCESSLIST,确认无长事务阻塞 - 监控
Innodb_buffer_pool_wait_free和Threads_connected,数值突增就是信号
流式读取只是把内存压力从客户端转移到了服务端 I/O 和锁调度上。真正难的不是“怎么读出来”,而是“读哪一段”“读多快”“读的时候别影响别人”——这三个问题没对齐,光换读取方式没用。


















