真正能扛住万级数据的批量插入必须配合事务控制与语句合并,单条多值INSERT需封装在BEGIN/COMMIT中,每批1000–5000行为宜,并注意max_allowed_packet、参数限制及主键有序性。

直接用 INSERT INTO ... VALUES (), (), ... 插入几千行以上,性能会断崖式下跌;真正能扛住万级数据的批量插入,必须配合事务控制、语句合并或专用工具,否则光是日志刷盘和锁争用就卡死。
用单条多值 INSERT + 事务封装(适合万行以内)
这是最通用、权限要求最低的方案,但必须把多行 VALUES 和显式事务绑在一起,否则效果打折。
- 别写成 1000 条独立
INSERT语句——每条都触发一次事务开销、日志写入和索引更新 - 把最多 1000–5000 行拼成一条
INSERT INTO t(a,b) VALUES (1,'x'),(2,'y'),...,再包在BEGIN TRANSACTION/COMMIT里 - MySQL 中如果
max_allowed_packet太小(默认 4MB),拼太长会报Packet too large,需提前调大 - SQL Server 对单条语句的参数个数有限制(约 2100 个),
VALUES列表实际受此约束,不是行数限制而是总占位符数
用 executemany() 配合连接库(Python/Java 等主流语言)
这是应用层最常用也最可控的方式,但很多人漏掉关键配置,导致性能只比逐条好一点。
- 必须关闭自动提交:
conn.autocommit = False,否则executemany()每批仍会隐式提交 - 显式调用
conn.commit()在所有批次之后,或按固定行数(如每 5000 行)分批 commit - PostgreSQL 的
psycopg2支持execute_batch()(带缓冲)比原生executemany()更快;MySQL 的mysql-connector-python同样建议设autocommit=False+ 手动 commit - 别传超大 list 直接塞进
executemany()——内存爆掉前,网络或驱动可能先报错;拆成子列表分批调用更稳
SQL Server 用 BULK INSERT 或 SqlBulkCopy(万行以上首选)
当数据量上十万、且你有文件落地或 .NET 环境时,绕过 T-SQL 解析路径才是真提速。
-
BULK INSERT要求文件对 SQL Server 服务账户可见,D:datainput.csv对客户端有效 ≠ 对 SQL Server 有效;UNC 路径(\servershare)更可靠 - 必须加
TABLOCK提示,否则默认行锁会让并发插入变成排队;没它,速度可能比普通 INSERT 还慢 - 导入前切恢复模式:
ALTER DATABASE db SET RECOVERY BULK_LOGGED,否则全量日志写入撑爆磁盘 - .NET 用
SqlBulkCopy时,BatchSize = 5000是经验值;设太大易 OOM,太小则事务开销回升;EnableStreaming = true只在源是IDataReader时生效,DataTable不走流式
避免踩坑:主键顺序、索引与 NULL 处理
这些细节不报错,但会让批量插入从“快”变“假快”甚至失败。
- 主键无序(比如 UUID 或随机 int)+ 大量插入 → 触发频繁页分裂,I/O 暴涨;能预排序就排,尤其 SQL Server 和 MySQL InnoDB
- 目标列定义为
NOT NULL,但源数据含空字符串或NULL字面量 → 整批失败,错误提示笼统(如Cannot insert the value NULL),得自己校验源头 - CSV 导入时字段含换行符或双引号,
FIELDTERMINATOR=','会解析错;SQL Server 2017+ 推荐用FORMAT='CSV'+FIRSTROW=2,但必须确认文件编码是 UTF-8 w/o BOM - 别在批量插入中途建/删索引——DDL 会阻塞 DML;应提前禁用非聚集索引,插完再重建
真正影响性能的从来不是语法怎么写,而是事务边界划在哪、日志怎么记、索引怎么让路;批量插入不是“多塞几行”,是主动让数据库少做点事。


















