当前位置:首页 > 文章列表 > 文章 > python教程 > sqlite3.Connection.blobopen 怎样分块读写 BLOB

sqlite3.Connection.blobopen 怎样分块读写 BLOB

来源:17golang原创 2026-11-06 21:49:48 0浏览 收藏

使用 Python 的 sqlite3.Connection.blobopen() 分块读写 BLOB,要先让目标行包含指定长度的 BLOB,再打开句柄,循环调用 write(chunk) 或 read(chunk_size)。写入前可用 zeroblob(n) 预留容量;句柄只能修改已有区域,不能自动扩容。

我会把它看作一种“固定容量、按块访问”的数据入口。这里以导入本地二进制附件为设计场景,说明我会怎样处理容量、rowid 和事务;示例供读者自行执行,不把未运行的代码描述成真实测量记录。

官方地址:https://docs.python.org/3.14/library/sqlite3.html#sqlite3.Connection.blobopen

先把 BLOB 当成固定长度的字节区域

blobopen("assets", "body", row_id) 指向一行的一个字段,而非一个数据库文件路径。前三个参数分别是表名、列名和整数 rowid;数据库符号名默认是 main。Python 的 Blob 接口从 3.11 加入。

我会使用 id INTEGER PRIMARY KEY 这样的普通 rowid 表来承载附件,将业务定位和字节访问分开。它不适用于 WITHOUT ROWID 表。别把业务字符串编号直接传成 rowid,也别以为调用 blobopen 就会替你插入新记录。

zeroblob(n) 表达 n 字节零值区域。这里 n 应来自可靠的长度信息,并受应用上传配额约束;不能为了方便直接相信外部传入的任意大小。

连接、assets行、body字段与固定容量之间的静态结构
结构说明图:行定位分组说明 rowid 与 assets 行的关系,字节区域分组说明 body、zeroblob 和 Blob 句柄的关联,不表示操作顺序。

我会在哪些场景选择分块接口

如果附件较大,或者业务只需要读取某个范围,我更倾向于保留一个有限大小的缓冲区,避免先把完整字段变成 Python bytes。代价是容量要预先确定,句柄和事务也需要更仔细地管理。

对于很小的二进制值,参数化 INSERT 或 UPDATE 往往更直观;如果输入长度未知、需要不断追加,固定长度 BLOB 就未必是最合适的模型。不能仅凭“使用了分块接口”就声称数据库吞吐一定更高。

一个有完整收尾的读写示例

准备 Python 3.11 及以上环境和一份稳定的 sample.bin。下面的函数在独立连接上管理事务,写入新行;若数据库已有同名表,需确保其结构与示例一致。不要直接对不熟悉的业务库运行示例。

import sqlite3
from pathlib import Path

CHUNK_SIZE = 64 * 1024

def store_file(source_path, database_path):
    # 输入应是导入期间不会变化的本地普通文件
    with Path(source_path).open("rb") as source:
        source.seek(0, 2)
        expected = source.tell()
        source.seek(0)
        # 显式 SQL 管理事务,兼容 Python 3.11 的连接参数
        con = sqlite3.connect(database_path, isolation_level=None)
        try:
            con.execute("""CREATE TABLE IF NOT EXISTS assets (
                id INTEGER PRIMARY KEY,
                body BLOB NOT NULL
            )""")
            con.execute("BEGIN")
            cursor = con.execute(
                "INSERT INTO assets(body) VALUES(zeroblob(?))", (expected,))
            row_id = cursor.lastrowid
            cursor.close()
            written = 0
            # 关闭句柄之后再提交,异常不能留下半成品记录
            with con.blobopen("assets", "body", row_id) as blob:
                while True:
                    chunk = source.read(CHUNK_SIZE)
                    if not chunk:
                        break
                    if written + len(chunk) > len(blob):
                        raise ValueError("源文件比预留容量更大")
                    blob.write(chunk)
                    written += len(chunk)
                if written != expected:
                    raise ValueError("源文件长度发生变化")
            con.execute("COMMIT")
            return row_id
        except BaseException:
            # 取消或读写错误都回滚当前事务,再向调用者报告
            if con.in_transaction:
                con.execute("ROLLBACK")
            raise
        finally:
            con.close()

def count_blob_bytes(database_path, row_id):
    # 按块消费数据;实际业务可在循环里计算摘要或解析二进制
    con = sqlite3.connect(database_path, isolation_level=None)
    try:
        total = 0
        with con.blobopen("assets", "body", row_id, readonly=True) as blob:
            while True:
                chunk = blob.read(CHUNK_SIZE)
                if not chunk:
                    break
                total += len(chunk)
        return total
    finally:
        con.close()

# 示例调用会创建本地数据库,路径应由调用方明确提供
if __name__ == "__main__":
    row_id = store_file("sample.bin", "assets-demo.db")
    print("row_id:", row_id)
    print("bytes_read:", count_blob_bytes("assets-demo.db", row_id))

缓冲区选 64 KiB 只是示例取值,不是性能结论。写入时记录实际字节数,并将它与预留容量比较;读取时将每块立即消费,避免再把所有块追加到一个列表中。代码中的 bytes_read 只报告消费量,不是内容一致性证明。

源文件若在导入期间被修改,即便长度不变,也可能得到不一致的内容。因此这个示例明确要求稳定输入;严格场景应先准备不可变快照或受应用控制的暂存文件。长度比较只能发现部分变化,不能代替这项约束。

不能把分块写入当成追加文件

情况需要理解的限制我的处理方式
写入超过长度Blob.write 不能扩容先检查剩余容量,失败时回滚
readonly=True 后写入句柄按只读方式打开按任务选择权限,不把读句柄用于写操作
预留太大却只写一部分剩余区域仍然存在核对实际写入量,避免把零填充尾部当成有效数据
目标行不存在句柄不创建行从 INSERT 的 lastrowid 取得本次行定位

偏移量的单位是字节。若只想改固定范围,可以先 seek(offset) 再写入;长度仍然必须合适。若业务需要调整整体长度,应在关闭旧句柄之后用 SQL 重新安排字段容量,而不是在旧句柄上期待“追加成功”。

SQLite 还限制某些带索引或约束的列进行增量写入,所以示例把 body 保持为普通 BLOB 列。设计时应核对表结构,不把所有打开失败都归因于 Python 版本。

资源与事务分别承担什么责任

我更在意的是两个边界:with blob 负责释放句柄,显式事务负责这一批数据库修改是否保留。连接仍需独立关闭。关闭句柄本身不能替代应用层“导入完整才提交”的决定。

示例先关闭写句柄再执行 COMMIT;出错时回滚新插入的行。实际 SQLite 数据库同一时刻只有一个写事务,长时间导入会影响其他写入者,因此我不会在持有写事务时等待交互输入或远程下载。

打开句柄期间,还应避免对同一行执行其他 UPDATE、DELETE 或产生相应冲突副作用。SQLite 可能把该句柄标记为失效;失效不是“之前所有写入已自动回滚”。这也是我把异常交还给外层事务决定的原因。

Blob句柄关闭、事务提交回滚与连接释放的静态责任关系
结构说明图:字节访问分组中的 Blob 负责分块读写与句柄关闭;事务和连接分组分别承担提交回滚与连接释放责任。

选择清单与常见问题

  • 长度已知且需要局部或分块访问:考虑 blobopen。
  • 字段很小:优先选择简单的参数化 SQL。
  • 长度未知或频繁追加:先重新评估存储模型与暂存方案。
  • 存在并发写入:明确事务时长、失败处理和输入稳定性。

为什么不能像文件一样一直 write?

打开的是已存在、长度固定的数据库值。写入只改变其中的内容,不能让它自动增长。

分块 read 后再 join 是否还节省内存?

如果把所有块都保留下来再拼接,应用仍会持有完整内容。分块的收益依赖消费者及时处理并释放每块。

读取数量相同,是否说明数据完整一致?

不能。字节数相同不代表内容相同;有完整性要求时,应在稳定输入基础上定义摘要或业务格式检查。

对我来说,这个接口的价值是把“行中固定字节区域”暴露成可定位的对象。选型时同时接受固定容量、事务占用和句柄生命周期,分块读写才有清楚的适用范围。

参考资料:Python sqlite3 接口;SQLite 增量 BLOB 打开规则;固定长度写入规则;SQLite 事务;zeroblob 函数。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Python contextlib.suppress 怎样只忽略指定的单一异常Python contextlib.suppress 怎样只忽略指定的单一异常
上一篇
Python contextlib.suppress 怎样只忽略指定的单一异常
csv.DictWriter 怎样处理多余字段
下一篇
csv.DictWriter 怎样处理多余字段
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    917次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    923次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1014次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    999次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    821次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码