当前位置:首页 > 文章列表 > 文章 > python教程 > Python sqlite3 with 代码块退出后为什么没有提交数据

Python sqlite3 with 代码块退出后为什么没有提交数据

来源:17golang原创 2026-09-08 05:31:09 0浏览 收藏

如果 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上下文管理器的自动处理逻辑有冲突,只有在上下文块内没有抛出异常的前提下,改动才会被自动提交。
Python sqlite3 连接上下文、待提交事务与数据库文件之间的静态关系图
图1:连接上下文只包住事务处理,提交或回滚之后仍需单独管理连接关闭。

先区分事务模式,再决定 commit 写法

“要不要写 commit()”取决于连接的事务控制方式。现代 Python 文档推荐使用 autocommit 明确表达策略;如果项目仍使用默认的传统控制,则要继续看 isolation_level

配置写入表现排查建议
autocommit=False事务持续打开,需显式提交或回滚把一个业务批次包在 with con: 或显式 commit()
autocommit=True使用 SQLite 自动提交,commit() 不产生额外动作不要把 commit() 当成“刷新查询”的开关
isolation_level=None传统模式下不隐式开启事务需要原子批量写入时显式执行 BEGINcommit()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), ["甲", "乙"]))
Python sqlite3 多线程中工作线程、独立连接与写事务边界的关系图
图2:线程归属、连接资源和写事务应分别建模;独立连接比共享连接更容易判断提交责任。

把“写入但看不到”收敛成检查清单

最后可以用一个独立连接验证结果,避免把当前连接里的可见数据误认为已经落盘:

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:,以及代码块是否因异常走了回滚路径。
  • 检查是否在查询前误用了另一个数据库路径,尤其是相对路径。
  • 检查 autocommitisolation_levelcheck_same_thread 的实际值。
  • 多线程场景用独立连接查询,并让每个连接在所属线程完成关闭。

这几项中,最常被忽略的是“连接没有关闭”和“事务没有提交”是两件事。关闭连接前仍有待处理变更时,不能指望它替你保存;相反,已经提交的数据也不需要靠关闭动作才能被其他连接看到。

常见问题

with con 会自动关闭 sqlite3 连接吗?

不会。它只在离开代码块时提交或回滚事务,连接需要显式调用 close(),也可以把连接关闭放进自己的资源管理结构。

为什么 commit() 调用了却没有变化?

先看是否处于 autocommit=True,或当前根本没有打开事务;这两种情况下 commit() 都可能没有可见的额外动作。

check_same_thread=False 能解决多线程写入吗?

它只关闭线程归属检查,不能解决并发写入的同步、事务互相覆盖和连接关闭责任。除非有明确的锁策略,否则优先给每个线程创建连接。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go 导入 internal 包被拒绝时怎么按目录规则定位Go 导入 internal 包被拒绝时怎么按目录规则定位
上一篇
Go 导入 internal 包被拒绝时怎么按目录规则定位
Go 嵌入静态资源后为什么 os.Stat 找不到它
下一篇
Go 嵌入静态资源后为什么 os.Stat 找不到它
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    19次使用
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    175次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    110次使用
  • AI Prompt Library:免费AI提示词库,助力ChatGPT高效创作与营销
    AI Prompt Library
    探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
    37次使用
  • Generrated:DALL·E 2/3 AI绘画提示词灵感库与图像对比平台
    Generrated
    Generrated汇集9300+张DALL·E生成图像及对应提示词,支持查看完整图集、对比DALL·E 2与3版本差异,是AI绘图新手学习Prompt设计与获取创作灵感的实用工具。
    17次使用