to_sql()默认逐行插入,性能极差;必须确认引擎是否支持executemany模式,显式指定method='multi'并合理设置chunksize(如50000),配合dtype声明、时间列标准化及服务端配置调优才能实现高效批量写入。

用 to_sql() 写入前必须确认 SQLAlchemy 引擎是否支持批量插入
默认的 to_sql() 会逐行 INSERT,性能极差。关键在于引擎是否启用 executemany 模式——这取决于底层 DBAPI 是否支持(如 pymysql 支持,mysqlclient 默认不启用)。
- 显式指定
method='multi'可触发多值 INSERT(MySQL 5.7+ 支持),大幅提升速度:df.to_sql('table', engine, method='multi', chunksize=10000) - 若用
pymysql,还需在创建引擎时加参数:connect_args={'autocommit': True},否则事务开销吃掉性能 - 避免用
mysqlclient+method=None(默认),它会生成 N 条单行 INSERT,10 万行可能耗时分钟级
字段类型不匹配会导致 to_sql() 静默失败或截断数据
Pandas 的 object 列写入 MySQL 时,to_sql() 默认映射为 VARCHAR(255),但实际长度可能超限;时间列若含 NaT 或时区信息,也会被转成空字符串或报错。
- 提前用
dtype参数显式声明列类型:dtype={'col1': sqlalchemy.String(500), 'col2': sqlalchemy.DateTime()} - 对含 NaN 的数值列,MySQL 的
INT不接受 NULL,需改用Integer(SQLAlchemy 类型,支持 NULL)或设nullable=True - 时间列务必先用
pd.to_datetime()标准化,并处理 NaT:df['time'] = df['time'].fillna(pd.NaT)
并发写入或表已存在时,if_exists 参数选错会清空数据
if_exists='replace' 不仅删表重建,还会丢掉索引、外键、注释等元数据;if_exists='append' 在高并发下可能因主键冲突失败,而非跳过重复。
- 安全追加:先用
engine.execute("INSERT IGNORE ...")或ON DUPLICATE KEY UPDATE替代append,需手写 SQL - 避免
replace:除非明确要重置表结构,否则用if_exists='append'+ 提前建好表(含索引和约束) - 检查目标表是否存在:
engine.dialect.has_table(engine, 'table_name'),再决定初始化逻辑
大文件分块写入时,chunksize 设太小反而更慢
chunksize=1000 对百万行数据会产生上千次 round-trip,网络和事务开销远超收益;设太大则内存溢出或锁表太久。
- 实测经验:MySQL 默认 max_allowed_packet=64M,按每行约 1KB 估算,
chunksize=50000较稳妥 - 配合
method='multi'和chunksize,单次 INSERT 最多塞 10 万行(受 MySQLmax_allowed_packet限制) - 写入前关闭 autocommit 并手动 commit:
engine.execute('SET autocommit = 0'),否则每个 chunk 都提交一次事务
真正卡点往往不在代码写法,而在 MySQL 服务端配置——比如 innodb_buffer_pool_size 过小会导致大批量写入时频繁刷盘,比 Python 层调优影响更大。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











