Python sqlite3 怎样用 URI 参数打开只读连接
如果程序只是读取一份已经准备好的 SQLite 数据库,普通的 sqlite3.connect("data/app.db") 并不能表达“文件必须存在、连接不能写入”这两个要求。SQLite 默认可能创建缺失文件,排查配置错误时就容易把一个空库当成成功连接。
更明确的写法是使用 URI 参数:sqlite3.connect("file:data/app.db?mode=ro", uri=True)。其中 file: 表示 SQLite URI,mode=ro 表示只读打开,uri=True 告诉 Python 把第一个参数按 URI 解释。文件不存在时连接应直接失败,连接建立后执行写操作也会被拒绝。
官方文档:https://docs.python.org/3/library/sqlite3.html
uri=True不是只读开关;没有它,mode=ro只是普通字符串的一部分。mode=ro要求目标数据库已经存在,不会因为路径写错而悄悄创建新库。- 只读连接仍然可以执行
SELECT;真正的INSERT、UPDATE或 DDL 会在写入阶段失败。
先把只读连接拆成三个条件
这个问题常常不是 Python 语法错了,而是三个层次混在了一起。file: 是 SQLite URI 的方案前缀,负责让后面的路径和查询参数组成一个 URI;mode=ro 是 SQLite 的打开模式,负责要求只读;uri=True 是 Python sqlite3.connect() 的参数,负责启用 URI 解析。
因此,下面两种写法的含义不同:
import sqlite3
# 这里明确启用 URI 解析;mode=ro 才会被 SQLite 当作只读参数。
conn = sqlite3.connect("file:data/app.db?mode=ro", uri=True)
conn.close() # 连接用完后主动释放文件句柄。
import sqlite3
# 没有 uri=True 时,不要把整个字符串误认为已经启用了只读模式。
conn = sqlite3.connect("file:data/app.db?mode=ro")
conn.close() # 这不是本文要求的 URI 只读连接。

用 mode=ro 打开已经存在的数据库
最小可用代码可以先固定一个相对路径:
from pathlib import Path
import sqlite3
db_uri = "file:data/app.db?mode=ro"
conn = None
try:
# uri=True 让 SQLite 读取 mode=ro,而不是把它当成普通文件名。
conn = sqlite3.connect(db_uri, uri=True)
rows = conn.execute("SELECT id, name FROM users ORDER BY id").fetchall()
for row in rows:
print(row)
except sqlite3.OperationalError as exc:
# 连接阶段或查询阶段的 SQLite 错误都保留原始信息,便于定位路径和权限问题。
raise RuntimeError(f"只读数据库访问失败: {exc}") from exc
finally:
if conn is not None:
conn.close() # 只读不等于不占用句柄,连接仍然需要关闭。
示例中的 Path 只是提醒路径应由配置决定;真正传给 connect() 的值仍是带 file: 前缀和查询串的 URI。读取失败时保留原始 OperationalError,比只输出“数据库不可用”更容易区分文件路径、权限和 SQL 问题。
文件不存在时,为什么应该在连接阶段失败
普通路径连接的一个常见误区是:程序启动后看到一个新的 app.db,就以为部署成功。实际上,路径拼写错误、挂载目录没有准备好、工作目录改变,都可能让程序打开了一个刚创建的空文件。
mode=ro 把这个边界前移到连接阶段。目标不存在时,SQLite 不应创建数据库,而是返回打开失败的 OperationalError。这正是只读数据目录适合使用 URI 的原因:缺文件会暴露出来,而不会被“自动初始化”掩盖。
import sqlite3
def open_existing_readonly(path: str) -> sqlite3.Connection:
# 只读连接要求部署先准备好数据库文件;不会替缺失文件创建空库。
uri = f"file:{path}?mode=ro"
return sqlite3.connect(uri, uri=True)
try:
conn = open_existing_readonly("data/app.db")
except sqlite3.OperationalError as exc:
# 这里优先检查工作目录、挂载路径和文件权限,而不是先重试查询。
raise RuntimeError(f"无法打开既有 SQLite 文件: {exc}") from exc
else:
conn.close() # 成功建立连接后也要释放资源。
这段代码适合表达“文件必须由部署步骤准备好”的约束。若路径来自用户输入或包含 ?、# 等 URI 保留字符,不能简单拼接字符串,应先按照 SQLite URI 规则对路径部分进行编码,并把查询参数放在编码后的路径之后。
写入被拒绝,不代表读取失败
只读连接的另一个排查现场是:SELECT 正常,但第一次写入时报 attempt to write a readonly database 一类错误。这通常说明 URI 已经按预期生效,问题不在连接建立,而在代码越过了只读边界。
import sqlite3
conn = sqlite3.connect("file:data/app.db?mode=ro", uri=True)
try:
# 查询属于只读连接允许的操作。
row = conn.execute("SELECT count(*) FROM users").fetchone()
print("当前记录数:", row[0])
# 这个写操作应被 SQLite 拒绝,用来暴露误写路径。
conn.execute("INSERT INTO users(name) VALUES (?)", ("demo",))
except sqlite3.OperationalError as exc:
# 生产代码应记录 SQL 场景,但不要吞掉只读约束错误。
raise RuntimeError(f"只读连接拒绝写入: {exc}") from exc
finally:
conn.close() # 无论查询还是写入失败,都关闭连接。
这里不要把只读 URI 改成普通路径来“修复”错误。先确认调用方是否本来就应该写入;如果需要写入,应把写库职责放到拥有写权限的连接或服务中,而不是让一个只读数据源偷偷承担写操作。
相对路径、绝对路径和工作目录要分开判断
URI 只读模式能约束打开方式,但不能替你决定相对路径从哪里开始解析。file:data/app.db?mode=ro 依赖当前工作目录;同一个服务在本地启动、进程管理器启动和容器启动时,工作目录可能不同。
排查时可把路径解析和 URI 拼装分开记录:
from pathlib import Path
from urllib.parse import quote
import sqlite3
db_path = Path("data/app.db").resolve()
# quote 只处理路径部分,保留斜杠,避免特殊字符被误当成 URI 查询语法。
encoded_path = quote(str(db_path), safe="/:\\")
db_uri = f"file:{encoded_path}?mode=ro"
conn = sqlite3.connect(db_uri, uri=True)
try:
conn.execute("SELECT 1").fetchone()
finally:
conn.close() # 只做探测也要及时关闭连接。
这个版本适合配置系统已经给出绝对路径的场景。Windows 路径、网络挂载和包含特殊字符的路径应在目标运行环境中按 SQLite URI 文档单独确认;不要因为本机相对路径可用,就假设服务部署目录也一致。

上线前按这张清单复查
| 检查点 | 应该看到的结果 | 异常时先看哪里 |
|---|---|---|
| URI 形式 | 以 file: 开头,并带 mode=ro | 配置拼接和路径编码 |
| Python 参数 | sqlite3.connect(..., uri=True) | 是否漏传 uri=True |
| 文件存在性 | 缺失文件在连接阶段失败 | 工作目录、挂载点、部署产物 |
| 查询行为 | SELECT 可以执行 | 文件权限、数据库损坏、SQL |
| 写入行为 | 写操作被明确拒绝 | 调用方是否误用了只读连接 |
| 资源释放 | 所有分支最终调用 close() | 异常处理和连接生命周期 |
归纳起来,Python sqlite3 的 URI 只读连接写法就是 file:数据库路径?mode=ro 配合 uri=True。先用它保证“文件必须存在”,再用查询成功和写入拒绝分别确认读取路径与只读边界,最后把绝对路径解析和连接关闭纳入部署代码。
常见问题
只传 mode=ro,不传 uri=True 可以吗?
不建议这样做。uri=True 是 Python 端启用 URI 解析的开关;缺少它时,查询串不会按本文需要的 SQLite URI 参数处理。
只读连接能执行事务吗?
可以执行不改变数据的查询,但事务语义不能把写入权限变出来。是否显式开启事务应服从应用的查询模式,写操作仍会被只读数据库拒绝。
文件不存在时想自动创建怎么办?
那是初始化或写入职责,不应和只读连接混在一起。先由部署或初始化流程创建并准备数据库,再用 mode=ro 提供读取连接,能更早暴露环境缺失。
Java jcmd 怎样按线程状态筛选定位阻塞
- 上一篇
- Java jcmd 怎样按线程状态筛选定位阻塞
- 下一篇
- Python 3.15 pathlib mkdir 怎样设置中间目录权限
-
- 文章 · python教程 | 25分钟前 | Python logging traceback logger.exception exc_info
- Python logging 怎样让异常堆栈保留上下文
- 421浏览 收藏
-
- 文章 · python教程 | 29分钟前 | python · Python 对象比较 Field dataclasses Compare
- Python dataclasses 怎样让字段不参与比较
- 341浏览 收藏
-
- 文章 · python教程 | 31分钟前 | python · Python 资源关闭 contextlib ExitStack
- Python contextlib 怎样把多个资源组合关闭
- 333浏览 收藏
-
- 文章 · python教程 | 33分钟前 | 标准库 · 编码 · python · Python 文件编码 pathlib UnicodeDecodeError Path.read_text
- Python pathlib.Path.read_text 怎样避免默认编码导致乱码
- 220浏览 收藏
-
- 文章 · python教程 | 33分钟前 | python · 键值存储 空间回收 Python shelve dbm reorganize
- Python shelve 怎样回收删除键留下的空间
- 394浏览 收藏
-
- 文章 · python教程 | 40分钟前 | 默认值 · python · Python Field 可变默认值 default_factory dataclasses
- Python dataclasses 怎样用 default_factory 避免共享可变默认值
- 136浏览 收藏
-
- 文章 · python教程 | 46分钟前 | python · 工程实践 · Python 浮点数 math.isclose 相对误差 绝对误差
- Python math.isclose 怎样同时控制相对误差和绝对误差
- 278浏览 收藏
-
- 文章 · python教程 | 48分钟前 | Python mkdir pathlib parent_mode
- Python 3.15 pathlib mkdir 怎样设置中间目录权限
- 330浏览 收藏
-
- 文章 · python教程 | 1天前 | Python教程 · 接口设计 · Python functools dispatch register singledispatch
- Python functools.singledispatch 怎样按参数类型选择函数
- 417浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 551次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 603次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 629次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 583次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 409次使用
-
- Python sqlite3 Connection serialize 怎么导出数据库快照:备份窗口、内存占用与恢复校验
- 2026-08-26 501浏览
-
- Python监控网页状态:requests异常处理实战
- 2026-05-29 501浏览
-
- TensorFlow模型部署为API的TF Serving方法
- 2026-05-26 501浏览
-
- Python字符串编码转换:encode与decode详解
- 2026-05-16 501浏览
-
- TensorFlow裁剪无用算子方法详解
- 2026-05-15 501浏览

