Python sqlite3 with 代码块退出后为什么没有提交数据
来源:17golang原创
时间:2026-09-08 05:31:09 488浏览 收藏
如果 Python 的 sqlite3 写入在 with 代码块后仍然查不到,先不要把问题归咎于 SQLite“没有保存”。最常见的原因是:退出的并不是一个打开事务的连接上下文,或者你把“提交”误当成了“关闭连接”。正确做法是先确认事务模式,再决定是否显式调用 commit();需要跨线程时,还要让连接归属和写入同步保持清楚。
with con:正常退出时提交、异常退出时回滚,但不会自动关闭con。autocommit=True下每条写入按 SQLite 自动提交处理,commit()没有额外作用。- 默认
check_same_thread=True,多线程应优先采用“每线程一个连接”,不要共享一个写连接。
with sqlite3.connect() 到底提交了什么
连接对象实现了上下文管理器,但它的职责很窄:只处理代码块结束时的事务提交或回滚。下面的写法可以提交插入结果,但连接仍然存在,真正关闭它仍要调用 close()。
import sqlite3
con = sqlite3.connect("notes.db")
try:
con.execute("CREATE TABLE IF NOT EXISTS note(id INTEGER PRIMARY KEY, body TEXT)")
with con:
# 代码块正常结束,当前打开的事务会提交
con.execute("INSERT INTO note(body) VALUES (?)", ("会议记录",))
finally:
# with 不负责释放连接,关闭前保留显式的资源边界
con.close()
这里有两个容易混淆的点。第一,with con: 不会凭空创建事务;它离开时只处理已经打开的事务。第二,若代码块中抛出未捕获异常,连接会回滚这次事务,异常本身仍会继续向外抛出。因此“代码块退出了”不等于“数据一定提交了”,还要看退出路径和连接当时是否处于事务中。
当你使用Python sqlite3模块的with语句包裹数据库连接操作,退出上下文后发现写入的数据没有落库,核心原因是sqlite3默认的事务提交行为和with上下文管理器的自动处理逻辑有冲突,只有在上下文块内没有抛出异常的前提下,改动才会被自动提交。

先区分事务模式,再决定 commit 写法
“要不要写 commit()”取决于连接的事务控制方式。现代 Python 文档推荐使用 autocommit 明确表达策略;如果项目仍使用默认的传统控制,则要继续看 isolation_level。
| 配置 | 写入表现 | 排查建议 |
|---|---|---|
autocommit=False | 事务持续打开,需显式提交或回滚 | 把一个业务批次包在 with con: 或显式 commit() 中 |
autocommit=True | 使用 SQLite 自动提交,commit() 不产生额外动作 | 不要把 commit() 当成“刷新查询”的开关 |
isolation_level=None | 传统模式下不隐式开启事务 | 需要原子批量写入时显式执行 BEGIN、commit() 和 rollback() |
一个稳妥的批量写入函数应该把成功和失败路径写在一起,让调用方知道何时数据已经交给数据库:
def save_notes(path, rows):
con = sqlite3.connect(path)
try:
# 一个函数调用对应一个事务,失败时整体回滚
with con:
con.executemany(
"INSERT INTO note(body) VALUES (?)",
((row,) for row in rows),
)
except sqlite3.Error:
# 让上层看到数据库错误,不把失败伪装成成功
raise
finally:
# 提交/回滚与关闭是两个独立动作
con.close()
如果设置了 isolation_level=None,不要只加一个 with con: 就期待它替你建立批量事务;应明确执行 BEGIN,并在异常时调用 rollback()。反过来,如果使用 autocommit=True,每次写入的边界已经不同,重复调用 commit() 只会让代码看起来更复杂。
多线程时别把提交问题误判成线程问题
当写入代码放到线程池后,常见现象是一个线程能查到,另一个线程却报错或看不到数据。默认情况下,连接只能由创建它的线程使用,跨线程会触发 ProgrammingError。把 check_same_thread=False 打开只能绕过这层检查,并不会自动替你串行化写操作。
更容易维护的方案是每个工作线程创建自己的连接,主线程只负责汇总结果;若业务必须共享连接,就要使用锁把写事务包住,并明确谁负责关闭连接。
import sqlite3
from concurrent.futures import ThreadPoolExecutor
def write_one(path, body):
con = sqlite3.connect(path)
try:
with con:
# 连接由当前工作线程创建和使用,避免跨线程复用
con.execute("INSERT INTO note(body) VALUES (?)", (body,))
finally:
con.close()
with ThreadPoolExecutor(max_workers=2) as pool:
# 每个任务独立拥有连接,提交边界也独立
list(pool.map(lambda text: write_one("notes.db", text), ["甲", "乙"]))

把“写入但看不到”收敛成检查清单
最后可以用一个独立连接验证结果,避免把当前连接里的可见数据误认为已经落盘:
def count_notes(path):
con = sqlite3.connect(path)
try:
# 独立连接读取,验证上一个连接是否真正提交
return con.execute("SELECT COUNT(*) FROM note").fetchone()[0]
finally:
con.close()
排查时按这个顺序看,通常很快就能定位:
- 写入后检查
con.in_transaction,确认是否还有未提交事务。 - 确认是否真的进入了
with con:,以及代码块是否因异常走了回滚路径。 - 检查是否在查询前误用了另一个数据库路径,尤其是相对路径。
- 检查
autocommit、isolation_level和check_same_thread的实际值。 - 多线程场景用独立连接查询,并让每个连接在所属线程完成关闭。
这几项中,最常被忽略的是“连接没有关闭”和“事务没有提交”是两件事。关闭连接前仍有待处理变更时,不能指望它替你保存;相反,已经提交的数据也不需要靠关闭动作才能被其他连接看到。
常见问题
with con 会自动关闭 sqlite3 连接吗?
不会。它只在离开代码块时提交或回滚事务,连接需要显式调用 close(),也可以把连接关闭放进自己的资源管理结构。
为什么 commit() 调用了却没有变化?
先看是否处于 autocommit=True,或当前根本没有打开事务;这两种情况下 commit() 都可能没有可见的额外动作。
check_same_thread=False 能解决多线程写入吗?
它只关闭线程归属检查,不能解决并发写入的同步、事务互相覆盖和连接关闭责任。除非有明确的锁策略,否则优先给每个线程创建连接。
-
文章 · python教程 | 1小时前 | 并发 · 日志 · Python教程 · 多进程 · 排障 · Python Fork multiprocessing 多进程日志 spawn QueueHandler QueueListener300 收藏
-
107 收藏
-
文章 · python教程 | 3小时前 | 异步编程 · Python教程 · 超时处理 · 任务取消 · Python asyncio CancelledError TaskGroup wait_for shield441 收藏
-
475 收藏
-
468 收藏
-
286 收藏
-
270 收藏
-
488 收藏
-
298 收藏
-
459 收藏
-
文章 · python教程 | 14小时前 | 性能优化 · 多线程 · Python教程 · 日志处理 · Python logging queue.Queue 后台线程 QueueHandler QueueListener174 收藏
-
326 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习