flask写入性能瓶颈在数据库层和orm交互:禁用单条add+commit循环,改用原生批量插入;合理配置连接池(pool_size=cpu×2~4、pool_pre_ping=true);避免flush()/expire_all(),分批提交需独立事务;精简索引、用分区表。

Flask 本身不处理数据写入性能,慢的根源几乎都在数据库层和 Python 层的交互方式上。直接优化视图函数或加 async 关键字,对大批量 INSERT 几乎无效。
SQLAlchemy 的 add() 和 commit() 是最大瓶颈
add() 不是立即发 SQL,但每次调用都会把对象加入 session 缓存;commit() 才真正触发批量插入——可一旦数据量超几千条,session 缓存膨胀、ORM 对象构建开销、事务日志写入都会陡增。
- 单条
add()+commit()循环:O(n) 次往返,最慢,绝对要禁用 - 全部
add()后一次commit():内存占用飙升,可能 OOM,且事务锁表时间过长 - 正确做法是绕过 ORM,用原生批量插入:
from sqlalchemy import text db.session.execute( text("INSERT INTO logs (user_id, action, ts) VALUES (:uid, :act, :ts)"), [{"uid": d["uid"], "act": d["act"], "ts": d["ts"]} for d in batch_data] ) db.session.commit() - 更进一步:用
execute_many(如 psycopg2 的executemany)或 PostgreSQL 的COPY/ MySQL 的LOAD DATA INFILE
连接池配置不当会让写入雪上加霜
写入密集时,如果连接池太小(默认pool_size=5),大量请求会排队等连接;如果 pool_recycle 过短,空闲连接频繁重建,又触发 DNS 解析 + TCP 握手 + SSL 协商。
- 检查实际连接数:
SELECT count(*) FROM pg_stat_activity;(PostgreSQL)或SHOW PROCESSLIST;(MySQL) - 生产环境建议:
-
pool_size设为 CPU 核数 × 2 ~ 4(如 8 核设 16~32) -
pool_pre_ping=True替代pool_recycle,更轻量地验证连接有效性 -
connect_timeout=3显式设置,避免卡在 DNS 或网络不可达上
-
批量写入时别碰 flush() 和 expire_all()
有些开发者想“分段提交”防 OOM,于是写成:
for i in range(0, len(data), 1000):
batch = data[i:i+1000]
for d in batch:
db.session.add(Log(**d))
db.session.flush() # ❌ 错误:触发 INSERT 但不提交,还留着事务
db.session.expire_all() # ❌ 更错:清空所有对象状态,后续访问全触发 SELECT
-
flush()只同步到 DB,不释放锁、不归还连接,反而延长事务时间 -
expire_all()是调试/测试用的重载机制,生产写入中完全没必要,且代价极高 - 真需要分批:用独立事务 +
commit(),每批后显式db.session.remove()释放 session 状态
索引和表结构设计直接影响写入吞吐
很多人只关注查询索引,却忽略写入时索引维护成本。每新增一行,每个索引都要更新 B+ 树节点。- 写入前临时禁用非必要索引(如仅用于报表分析的复合索引),写完再重建
- 避免在写入频繁的字段上建过多索引,尤其是
TEXT、JSONB类型字段的全文索引 - 使用分区表(如按时间分区)可显著提升大表写入性能,PostgreSQL 和 MySQL 8.0+ 均支持
真正卡住大规模写入的,从来不是 Flask 路由或模板渲染,而是你没意识到:ORM 默认行为、连接池参数、事务粒度、索引数量这四者叠加后的隐性放大效应。改一处可能只快 10%,四者齐改,往往能从分钟级降到秒级。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











