应使用分块写入避免内存爆炸:通过设置chunksize和method='multi'参数,配合流式读取与手动事务控制,提升大表写入稳定性与性能。

用 to_sql 分块写入避免内存爆炸
直接调用 df.to_sql() 写入大表,极易触发 MemoryError 或数据库连接超时。根本原因是 Pandas 默认把整个 DataFrame 加载进内存再拼成 INSERT 语句——哪怕你只传 10 万行,底层也可能生成单条超长 SQL 或缓存全部参数。
正确做法是显式分块(chunking):
df.to_sql(
name='my_table',
con=engine,
if_exists='append',
index=False,
chunksize=5000, # 每次只处理 5000 行
method='multi' # 关键:启用多值 INSERT,大幅减少 SQL 调用次数
)-
chunksize不是越大越好:超过 1 万容易触发 MySQL 的max_allowed_packet或 PostgreSQL 的参数绑定上限 -
method='multi'在 SQLite 和 MySQL 上有效;PostgreSQL 需用method='psycopg2'(需安装psycopg2)或改用method=None+ 手动批量插入 - 别依赖
if_exists='replace'清空重写——它会先DROP TABLE,高并发下可能丢数据
流式读取 + 流式写入:避免一次性加载 CSV/Excel 到内存
如果你的源是大文件(比如 2GB CSV),用 pandas.read_csv() 全量读取再写库,第一步就崩了。
必须组合使用迭代读取和分块写入:
立即学习“Python免费学习笔记(深入)”;
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
for chunk in pd.read_csv('huge_file.csv', chunksize=10000):
chunk.to_sql(
name='target_table',
con=engine,
if_exists='append',
index=False,
chunksize=5000,
method='multi'
)-
pd.read_csv(..., chunksize=N)返回的是TextFileReader迭代器,每次只 hold 一个 chunk 的内存 - 两个
chunksize可不同:读取 chunk 大些(如 10000)减少 I/O 次数;写入 chunk 小些(如 5000)适配数据库限制 - Excel 文件同理,用
pd.read_excel(..., chunksize=N),但注意openpyxl引擎不支持 chunking,得换engine='xlrd'(仅旧版 xls)或改用calamine(新推荐)
绕过 Pandas 中间层:用原生 DB API 批量插入更稳
当 to_sql 遇到特殊类型(如 JSON 字段、数组、几何字段)或需要精细控制(如跳过某些行、动态生成 INSERT 语句),Pandas 的抽象反而成障碍。
直接走 SQLAlchemy Core 或数据库原生 API 更可靠:
from sqlalchemy import text
<p>with engine.connect() as conn:
for chunk in pd.read_csv('data.csv', chunksize=5000):</p><h1>转为字典列表,适配 execute() 的 executemany 模式</h1><pre class="brush:php;toolbar:false;"><pre class="brush:php;toolbar:false;"> data_dicts = chunk.to_dict(orient='records')
conn.execute(
text("INSERT INTO my_table (col1, col2) VALUES (:col1, :col2)"),
data_dicts
)
conn.commit()</code></pre>executemany 比循环单条 <code>execute快 10 倍以上,且不经过 Pandas 类型转换,避免NaT→None错误或时区丢失- PostgreSQL 推荐用
copy_from(psycopg2)或copy_expert,比 SQL 插入快一个数量级 - MySQL 可用
LOAD DATA INFILE,但要求文件在数据库服务器本地,且权限配置复杂
事务控制与错误恢复常被忽略
默认情况下,to_sql 每个 chunk 是独立事务。一旦第 10 个 chunk 失败,前 9 个已提交,无法回滚——这不是“流式写入”,是“断点续写噩梦”。
- 手动控制事务:用
engine.begin()包裹整个写入过程,失败则全部回滚 - 生产环境务必加重试逻辑,尤其网络不稳时:
conn.execute()报OperationalError: (MySQLdb._exceptions.OperationalError) (2013, 'Lost connection to MySQL server during query')很常见 - 不要假设数据库自增 ID 连续:批量插入后若需获取 ID,得用
RETURNING(PostgreSQL)或LAST_INSERT_ID()(MySQL),不能靠 Python 算
流式写入真正的难点不在“怎么写”,而在“写挂了怎么办”——事务边界、错误分类、幂等设计,这些没法靠一个 chunksize 参数解决。

















