当前位置:首页 > 文章列表 > 文章 > python教程 > Python sqlite3.Connection.set_progress_handler 怎么给长查询加中止点:回调频率、取消标记与连接复用
Python sqlite3.Connection.set_progress_handler 怎么给长查询加中止点:回调频率、取消标记与连接复用
SQLite 遇到大表扫描、复杂排序或临时表时,Python 调用方常常只能等 execute() 返回。sqlite3.Connection.set_progress_handler() 提供了一个更可控的中止点:SQLite 每执行一批虚拟机指令就调用一次 Python 回调,回调返回非零值时当前语句会被中止。它适合做“用户取消”和“应用层预算”检查,但不是精确的毫秒级超时器。
实践要点
- 第二个参数是指令间隔,不是毫秒;间隔越小,取消响应越快,回调开销也越明显。
- 回调里只做轻量状态读取,真正的清理和错误转换放在执行语句的外层。
- 连接进入连接池前要清除旧 handler,避免下一次借用连接时继承上一条查询的取消逻辑。
先把中止目标说清楚:取消一条查询,而不是杀掉连接
假设订单后台允许用户取消一次“按地区、状态和金额排序”的导出查询。我们希望取消只影响当前语句,连接还可以继续执行下一条查询。直接关闭连接虽然粗暴,但会让连接池拿到一个失效对象,也会把连接级资源回收和业务取消混在一起。

set_progress_handler(callback, n) 的 n 表示大约每执行 n 条 SQLite 虚拟机指令调用一次回调。回调返回 0 表示继续,返回非零值表示让 SQLite 终止当前语句。不同查询的指令数量不同,所以它不能直接换算成固定的时间。
最小实现:用线程安全标记让回调只负责判断
取消动作通常发生在另一个请求线程,或者由后台任务设置一个事件。回调应当尽量短:读取一个已经准备好的标记即可,不要在其中执行日志格式化、网络请求或再次访问同一个连接。
import sqlite3
import threading
cancelled = threading.Event()
def stop_when_cancelled() -> int:
return 1 if cancelled.is_set() else 0
def find_orders(conn: sqlite3.Connection, region: str) -> list[tuple]:
conn.set_progress_handler(stop_when_cancelled, 10_000)
try:
return conn.execute(
"""
SELECT order_id, amount
FROM orders
WHERE region = ? AND status = ?
ORDER BY amount DESC
""",
(region, "paid"),
).fetchall()
finally:
conn.set_progress_handler(None, 0)
这里的 finally 有两个作用:语句成功时清除 handler,语句被中止时也清除 handler。否则同一个连接下一次执行普通查询,仍可能读取已经置位的取消事件。
回调频率怎么定:先看响应窗口,再测开销
把 n 写成 1 并不等于“最及时”。长查询会频繁进入 Python 回调,解释器切换和事件读取可能反过来拖慢查询。把它调得很大又会让用户点击取消后等待更久。
更实用的做法是先给出一个中等间隔,例如 10_000,再用真实数据测三个指标:正常查询耗时、取消按钮到 OperationalError 的延迟、回调次数。SQLite 的虚拟机指令量随 SQL、索引和数据分布变化,不能把某次测试的间隔直接当成所有查询的时间预算。
progress_calls = 0
def progress() -> int:
global progress_calls
progress_calls += 1
return int(cancelled.is_set())
conn.set_progress_handler(progress, 10_000)
try:
rows = conn.execute("SELECT ...").fetchall()
except sqlite3.OperationalError as exc:
# 记录查询被中止;不要把所有 OperationalError 都当成用户取消
if cancelled.is_set():
raise RuntimeError("query cancelled by caller") from exc
raise
finally:
conn.set_progress_handler(None, 0)
异常边界:中止结果要和 SQL 错误分开
被 progress handler 中止的语句通常会以 sqlite3.OperationalError 的形式回到调用方。这个异常类型也可能代表锁冲突、语法问题或数据库损坏,因此不能只写一个宽泛的 except sqlite3.OperationalError 就返回“用户取消”。要结合取消事件、请求上下文或本次查询的状态字段判断。
如果业务要求取消后继续使用连接,先确认连接仍能执行一个轻量探针,再归还连接池。不要在 handler 回调内部关闭连接,也不要让回调抛出业务异常;异常转换应当发生在 execute() 外层。
连接复用的权限边界:注册和清除必须成对出现

连接池里的连接是共享资源,最容易出现的故障是 A 请求注册了 handler,查询完成后忘记清理,B 请求借到连接后突然被 A 的取消标记中止。把注册动作封装在查询函数内部,并在 finally 里清除,是比依赖调用方记忆更可靠的边界。
def run_with_cancel(conn, sql, params, event, step=10_000):
def progress():
return 1 if event.is_set() else 0
conn.set_progress_handler(progress, step)
try:
return conn.execute(sql, params).fetchall()
finally:
conn.set_progress_handler(None, 0)
# 归还连接前,确保下一个调用者看到的是干净连接
conn.execute("SELECT 1").fetchone()
如果项目用的是多线程访问连接,还要遵守连接创建时的线程约束,不要因为 handler 本身很短就忽略 check_same_thread 和连接池的借还规则。progress handler 解决的是语句中止,不会自动解决并发访问同一连接的问题。
日志审计和发布前检查:至少覆盖三条路径
排查这类功能时,日志里建议记录查询标识、handler 间隔、是否收到取消、执行结果和连接清理状态,不要把完整 SQL 参数直接写入日志。测试至少覆盖以下三条路径:
- 正常短查询:返回结果,handler 在
finally中清除,连接可以执行下一条语句。 - 长查询中途取消:回调返回非零,外层识别为调用方取消,连接探针成功。
- 真正的 SQL/锁错误:即使取消标记没有置位,也要保留原始数据库错误,不能误报成用户取消。
最后做一次连接复用测试:先运行一个会触发取消的查询,再用同一连接执行 SELECT 1 和一条普通业务查询。如果第二条查询仍被中止,说明清理时机或事件生命周期出了问题。
相关问题
set_progress_handler 能做精确的 500 毫秒超时吗?
不能把它当成精确计时器。它按虚拟机指令次数触发,适合检查取消标记或粗粒度预算;若需要严格的时间控制,应在应用层记录截止时间,并在回调中轻量判断。
回调返回非零后能否只取消当前游标、保留部分结果?
中止的是当前语句,调用方通常会收到异常,不能把它当成可靠的部分结果提交机制。需要断点续查时,应把查询拆成可记录游标的批次。
为什么不用直接调用 interrupt?
连接级中断适合明确拥有该连接的场景;progress handler 更方便把取消检查放进一次查询的生命周期,并在语句结束时清除。两者都要结合连接归属和并发模型选择。
这项 API 的价值不在于让 SQLite 突然具备数据库级超时,而在于给长查询增加一个可验证的协作式中止点。把回调保持轻量、把异常判断放在外层、把 handler 清理写进 finally,再用连接复用测试验收,才是可以长期维护的实现。
Java ReentrantLock 条件队列怎么避免虚假唤醒:await、signal 与队列状态核验
- 上一篇
- Java ReentrantLock 条件队列怎么避免虚假唤醒:await、signal 与队列状态核验
- 下一篇
- Go crypto/rand 生成短期令牌怎么做:编码长度、熵预算与过期校验
-
- 文章 · python教程 | 2小时前 | 并发 · 异常处理 · Python教程 · asyncio · Python 3.11 · Python asyncio CancelledError 结构化并发 TaskGroup gather ExceptionGroup
- Python asyncio.TaskGroup 取消异常怎么收敛:从 gather 迁移到结构化并发
- 379浏览 收藏
-
- 文章 · python教程 | 3小时前 | 并发 · 线程 · python · queue · 故障排查 · 优雅停机 生产者消费者 Python queue.ShutDown Queue.shutdown 线程协作
- Python queue.ShutDown 怎么结束生产者消费者:关闭语义、阻塞唤醒与兼容写法
- 321浏览 收藏
-
- 文章 · python教程 | 5小时前 | 日志 · python · 性能排查 · Python QueueHandler QueueListener 日志队列 日志阻塞
- Python logging.handlers.QueueHandler 生产环境怎么避免日志阻塞:队列满载与降级策略
- 164浏览 收藏
-
- 文章 · python教程 | 9小时前 | 日志 · logging · Python教程 · 生产运维 · QueueHandler · Python 优雅停机 logging QueueHandler QueueListener 日志不丢
- Python logging QueueHandler 停机时怎么保证日志不丢:队列排空、关闭顺序与异常兜底
- 469浏览 收藏
-
- 文章 · python教程 | 13小时前 | 容器 · 性能优化 · 并发编程 · Python教程 · 线程池 Python 3.13 os.process_cpu_count 容器配额 并发度
- Python 3.13 os.process_cpu_count 怎么选并发度:容器配额、默认值与线程池边界
- 197浏览 收藏
-
- 文章 · python教程 | 17小时前 |
- Python pathlib.Path.info 有什么用:文件类型缓存、stat 刷新与批量扫描性能
- 420浏览 收藏
-
- 文章 · python教程 | 19小时前 | 标准库 · 自动化 · 浏览器 · python · webbrowser · 默认浏览器 浏览器自动化 Python webbrowser.open 无界面环境
- Python webbrowser.open 为什么不等于浏览器自动化:默认浏览器、返回值与无界面环境边界
- 223浏览 收藏
-
- 文章 · python教程 | 20小时前 | 并发 · 日志 · python · asyncio · contextvars · 线程池 请求上下文 日志关联 Python contextvars asyncio Task
- Python contextvars 在异步任务中怎么传请求上下文:Task 边界、线程池与日志关联
- 234浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- ljg-skills
- ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
- 5280次使用
-
- MELO音乐
- MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
- 4791次使用
-
- UniScribe
- UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
- 4742次使用
-
- 剧云
- 剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
- 5002次使用
-
- 万象有声
- 万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
- 4944次使用
-
- MySQL 明明加了索引,为什么查询还是很慢?先查这 6 个点
- 2026-06-27 374浏览
-
- 接口返回的数据和数据库不一致怎么办?按数据生命周期排查
- 2026-06-27 398浏览
-
- Go语言操作redis数据库的方法
- 2023-01-07 214浏览
-
- Go单元测试对数据库CRUD进行Mock测试
- 2023-02-25 411浏览
-
- Beego中ORM操作各类数据库连接方式详细示例
- 2023-01-07 444浏览

