to_sql写入慢需分块处理:用chunksize强制分批,搭配method='multi'(PostgreSQL)或默认(MySQL),显式设if_exists='append',datetime需剥离时区并截断精度,必须复用带连接池的引擎,高吞吐场景可直连游标批量插入。

用 to_sql 一次写入太慢?别硬扛,得流式分块
直接调用 df.to_sql() 写入百万级 DataFrame 到 MySQL 或 PostgreSQL,常导致内存暴涨、连接超时、事务锁表。根本原因不是 Pandas 不行,而是默认把整张表当一个事务提交。生产环境必须控制单次写入量,靠 chunksize 参数切片是最轻量、最通用的解法。
-
chunksize不是“建议值”,是强制分批阈值——设为1000就意味着每 1000 行发起一次INSERT,避免单语句过长或 OOM - PostgreSQL 推荐搭配
method='multi'(批量 VALUES),MySQL 则保持默认(单条 INSERT)更稳,method='multi'在 MySQL 8.0+ 才真正生效 - 务必显式传
if_exists='append',否则默认'fail'会因表已存在直接报错ValueError: Table 'xxx' already exists.
如何避免 to_sql 遇到 datetime 时区或精度报错?
数据库对时间类型敏感,Pandas 的 datetime64[ns] 直接写入常触发 TypeError: Couldn't parse datetime 或字段被截断成秒级。关键不在格式化,而在 dtype 对齐和时区剥离。
- 写入前统一转为无时区:用
df['ts'] = df['ts'].dt.tz_localize(None)或.dt.tz_convert(None),避免 PostgreSQL 报timezone-aware datetime not supported - 若目标字段是
TIMESTAMP WITHOUT TIME ZONE(如 PostgreSQL 默认),但 Pandas 列含毫秒,需提前截断:df['ts'] = df['ts'].dt.floor('s'),否则可能被静默丢弃小数位 - SQL Server 用户注意:
datetime2支持纳秒,但datetime类型只支持 3 位毫秒,写入前用.dt.round('ms')更安全
连接池没配?to_sql 会反复建连,拖垮数据库
每次调用 to_sql 若传的是字符串 URL(如 'postgresql://u:p@h:5432/db'),SQLAlchemy 就新建一个连接,不复用、不回收,高并发下迅速打满数据库连接数。必须用带连接池的引擎实例。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 用
create_engine(url, pool_size=10, max_overflow=20)创建一次,全局复用;pool_pre_ping=True可自动剔除失效连接 - 不要在循环里反复
create_engine,哪怕加了echo=True看起来只是日志多——它真正在建物理连接 - 如果用的是 SQLAlchemy 2.0+,记得传
future=True,否则to_sql可能报ObjectDereferencedError(连接被提前关闭)
实时性要求高?绕过 Pandas,用原生游标批量插入
当每秒要推几千行、且不能容忍 to_sql 的 DataFrame → SQL 解析开销时,就得跳过 Pandas 封装,直连数据库游标。这不是“更高级”,而是权衡:放弃易用性,换确定性吞吐。
立即学习“Python免费学习笔记(深入)”;
- PostgreSQL 推荐
execute_batch(cursor, sql, data, page_size=1000)(来自psycopg2.extras),比逐行execute快 5–10 倍 - MySQL 用
cursor.executemany(sql, data)即可,但确保data是 list of tuples,不是 list of dicts(后者会额外解析) - 数据类型必须严格匹配:比如数据库字段是
NUMERIC(10,2),Python 里就得传decimal.Decimal,传 float 可能触发精度丢失或拒绝插入

















