batch_size并非越大越好,需兼顾数据库连接数、内存限制及参数上限;PostgreSQL建议控制在500–2000,避免单条SQL超1000组VALUES,并配合synchronous_commit=off等服务端调优。

batch_size 不是越大越好,得看数据库连接和内存的承受力
直接把 batch_size 设成 10000 看似很爽,但常导致 MemoryError 或数据库连接超时。PostgreSQL 默认单次参数上限是 65535 个绑定变量,MySQL 的 max_allowed_packet 默认才 4MB——插 5000 条含 JSON 字段的记录就可能触发截断或报错 Packet too large。
实操建议:
- 先用小批量(如
batch_size=100)跑通逻辑,再逐步加到 500–2000 区间测试 - 对 PostgreSQL,避免单条
INSERT ... VALUES (...), (...), ...超过 1000 组,否则解析开销陡增 - 用
psutil.Process().memory_info().rss监控写入时内存增长,超过 500MB 就该降 batch
用 executemany() 还是 execute() + VALUES tuple?关键看驱动和方言
很多人以为 executemany() 天然更快,但实际在 psycopg2(PostgreSQL)里它本质还是拼 SQL;而 MySQLdb / PyMySQL 对 executemany() 有预编译优化,但 SQLite 的 executemany() 反而比手拼 VALUES 慢 20%+(因额外循环开销)。
实操建议:
- PostgreSQL:优先用
execute("INSERT INTO t (a,b) VALUES %s", tuples)配合psycopg2.extras.execute_values() - MySQL:确认驱动版本 ≥ 1.1.0 后,
executemany()可安全使用,但需设cursor.execute("SET SESSION wait_timeout = 28800")防超时 - 避免在循环里反复调用
execute()插单条——哪怕加了commit()延迟,I/O 次数仍是瓶颈
autocommit=False 是基础,但 commit 时机必须手动控制
默认 autocommit=False 只是起点,如果每插 100 条就 conn.commit(),事务日志刷盘太频繁;但如果等全部写完再 commit,崩溃就全丢。更糟的是某些 ORM(如 SQLAlchemy ORM)默认每 session.add() 都隐式 flush,根本没走批量路径。
实操建议:
- 显式关闭 autocommit:
conn.autocommit = False,然后自己决定 commit 频率 - 按时间窗口或记录数双控:比如“每 500 条或 2 秒钟 commit 一次”,用
time.time()记上一次 commit 时间 - 绕过 ORM 直接用原始 cursor(
conn.cursor()),避免 SQLAlchemy 的bulk_insert_mappings()在大数据量下反因对象构造拖慢 3–5 倍
别忽略数据库端配置,Python 写得再快也卡在 WAL 或锁上
Python 层 batch 提升了网络和解析效率,但 PostgreSQL 的 WAL 写入、MySQL 的 binlog 刷盘、以及行锁等待,才是真正的吞吐天花板。曾见一个批量插入卡在 LockWaitTimeout,查出来是另一进程在对同一张表做 ALTER TABLE。
实操建议:
- PostgreSQL:临时调大
checkpoint_timeout和max_wal_size,插入前执行SET synchronous_commit = off(接受小概率丢数据) - MySQL:插入前设
SET unique_checks=0, foreign_key_checks=0,完事后恢复(仅限导入可信数据) - 确保目标表没有冗余索引——每多一个索引,batch 写入就要多做一次 B-tree 插入,5 个索引能让吞吐跌 40%
真正卡住的地方往往不在 Python 代码里,而在你没查过的 pg_stat_activity 或 SHOW PROCESSLIST 输出中。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











