批量插入应使用 INSERT INTO ... VALUES (...),(...),... 语法,配合显式事务、合理分批及数据库引擎优化,避免逐条执行和参数超限。

用 INSERT INTO ... VALUES (...), (...), (...) 一次性插多行
单条 INSERT 插一行,网络往返、解析开销大,速度天然慢。合并成一条语句批量插,能显著减少语句解析和事务日志写入次数。
常见错误是拼接时漏了括号、逗号错位,或单条值里含未转义的引号/特殊字符,导致 SQL 语法错误 ERROR 1064。
- MySQL 支持最多约 65535 个参数(不是行数),实际建议单次 ≤ 1000 行,避免包过大或超
max_allowed_packet - PostgreSQL 对
VALUES行数没硬限制,但超过几千行后计划器可能变慢,建议分批 ≤ 5000 - SQLite 默认有
SQLITE_MAX_VARIABLE_NUMBER限制(通常 999),必须分段;可用PRAGMA compile_options查看是否含MAX_VARIABLE_NUMBER=...
示例(MySQL):
INSERT INTO users (name, email) VALUES ('Alice', 'a@x.com'), ('Bob', 'b@x.com'), ('Charlie', 'c@x.com');
关闭自动提交 + 显式事务包裹批量操作
默认每条 INSERT 都是一次独立事务,每次都要刷盘(fsync)、写 binlog/redolog,I/O 成为瓶颈。关掉自动提交,把多条插入包进一个事务,只做一次持久化。
容易踩的坑:忘了 COMMIT,导致连接挂起、锁表、后续查询被阻塞;或者事务太大,触发 MySQL 的 innodb_log_file_size 溢出,报错 ERROR 1205(死锁)或 ERROR 1197(日志满)。
- MySQL:执行
SET autocommit = 0;后,必须配对COMMIT;;或用BEGIN; ... COMMIT; - PostgreSQL:用
BEGIN; ... COMMIT;,注意不要在函数或 prepared statement 里隐式开启事务导致嵌套 - SQLite:
BEGIN IMMEDIATE;比BEGIN DEFERRED;更早获取锁,适合高并发写入场景
不同数据库对批量插入的底层处理差异
同一条 INSERT ... VALUES 在不同引擎下性能表现可能差几倍——不是语法问题,是存储层机制决定的。
比如 MySQL 的 InnoDB 和 MyISAM 对批量插入的缓冲策略完全不同;PostgreSQL 的 COPY 实际比多值 INSERT 快一个数量级,但它不属于 SQL 标准语法,需客户端配合。
- InnoDB:开启
innodb_flush_log_at_trx_commit = 2可降低刷盘频率(牺牲极小可靠性),配合批量事务效果明显 - MyISAM:无事务,
INSERT DELAYED已废弃,不推荐用于新项目 - PostgreSQL:用
COPY users FROM STDIN是最快路径,但需要驱动支持(如 psycopg2 的copy_from),不能直接写在普通 SQL 字符串里 - SQLite:启用
PRAGMA journal_mode = WAL;+PRAGMA synchronous = NORMAL;能提升写入吞吐,但要注意 WAL 模式下读一致性逻辑变化
别忽略客户端与连接层的隐性开销
即使 SQL 写对了、事务也包好了,如果用 ORM(如 Django ORM、SQLAlchemy)默认逐条 save(),或 JDBC 的 PreparedStatement.addBatch() 没调 executeBatch(),实际还是发了 N 条请求。
更隐蔽的是连接池配置:若最大连接数设太小,批量任务排队等待连接,表面看是“插入慢”,实则是连接争用。
- Python + pymysql:确认用了
executemany(),而非循环调execute() - Java + JDBC:检查是否设置了
rewriteBatchedStatements=true(MySQL 特有优化,会把 batch 自动重写成多值INSERT) - Node.js + mysql2:用
connection.query('INSERT...', [rows])数组传参,而不是自己拼字符串 - 连接池 idle timeout 要大于单批事务执行时间,否则连接被回收再重建,白忙活
批量插入快不快,不只看 SQL 怎么写,得从客户端、驱动、协议、引擎、磁盘一起看。最常被跳过的,其实是确认「到底发了几条网络请求」——用 tcpdump 或数据库的 general log 一眼就能验真伪。

















