session.commit() 正常返回且未抛异常,才代表事务真正提交成功;它阻塞等待数据库确认写入完成,包括wal刷盘和主从同步等,而非仅发送请求。

Session.commit() 什么时候才算真正提交成功?
只有 Session.commit() 正常返回,且没有抛出异常,才代表事务已持久化到数据库。它不是“发了个请求”,而是阻塞等待数据库确认写入完成(包括 WAL 刷盘、主从同步等,取决于数据库配置)。如果中途网络断开、数据库崩溃或违反约束(如唯一键冲突),commit() 会抛出 IntegrityError、OperationalError 等异常,此时所有未提交的变更自动回滚。
常见错误:在 commit() 后继续操作已失效的对象(比如调用 obj.id),却没意识到对象可能因异常未被 flush;或者把 commit() 当成“尽力而为”,不检查异常就认为写入成功。
- 务必用
try/except包裹commit(),捕获SQLAlchemyError及其子类 - 不要依赖
session.add(obj)后立刻读取obj.id—— 这个 ID 只有在flush()或commit()后才生成(除非用了sqlite_autoincrement等特殊策略) - 避免在
commit()后继续修改已提交对象的属性并再次add()—— 它已是持久态,直接改属性 + 再次commit()即可
嵌套事务和 session.begin_nested() 的真实行为
session.begin_nested() 并不开启真正的数据库嵌套事务(PostgreSQL 支持 SAVEPOINT,MySQL 不支持),而是模拟一个可回滚的保存点(SAVEPOINT)。它只对当前 Session 有效,无法跨 session 或跨连接生效。
典型使用场景:在一个大事务中隔离某段逻辑(如批量导入中的单条记录校验失败),避免整个流程因局部错误中断。但它不能替代数据库原生事务隔离级别,也不解决并发写冲突。
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
- 必须配合
rollback()使用:调用begin_nested()返回的NestedTransaction对象,应在异常时显式调用其rollback() - 外层
commit()成功后,所有 SAVEPOINT 都自动释放;外层rollback()会连同所有嵌套点一并回滚 - SQLite 和 PostgreSQL 行为一致;MySQL(InnoDB)虽支持 SAVEPOINT,但 SQLAlchemy 在 autocommit=False 模式下对其兼容性较弱,建议优先用显式异常控制流
多线程或多协程下 Session 实例不能共享
每个线程或 asyncio 任务必须持有独立的 Session 实例。共用同一个 Session 会导致状态混乱:flush 缓冲区竞争、事务边界错乱、对象状态(pending/transient/attached)互相污染,最终引发 InvalidRequestError: This session is in 'committed' state 或静默数据丢失。
正确做法是按需创建、用完即关,而不是全局单例。Flask-SQLAlchemy 的 scoped_session 或 FastAPI 中的依赖注入,本质都是为每个请求/上下文绑定专属 Session。
- 绝不在
ThreadPoolExecutor里传入同一Session实例 - 异步场景下,不要在
async def函数里复用同步Session;应使用AsyncSession+async_engine - 手动管理时,确保
session.close()被调用(推荐用with上下文管理器)
autocommit=True 是原子性的反模式
设置 autocommit=True 会让每次 execute() 或 add() 后立即提交,彻底放弃事务控制能力。它仅适用于日志表插入、缓存刷新等“不关心一致性”的场景,绝不该用于涉及多个表更新、余额扣减、订单创建等需要原子性的业务。
更隐蔽的问题是:启用 autocommit=True 后,session.commit() 变成空操作,session.rollback() 抛出异常,session.begin() 失效 —— 整个 Session 生命周期管理逻辑全部崩坏。
- 默认就该保持
autocommit=False(SQLAlchemy 1.4+ 默认值) - 若误设为
True,即使写了try/except也拦不住部分写入成功、部分失败的“半截状态” - 想简化流程?用
session.begin()上下文管理器,而不是开关 autocommit
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










