如何避免SQLite3因OperationalError(数据库锁定)丢失数据变更?
解决SQLite数据库锁定时的变更丢失问题
针对你遇到的数据库锁定导致数据丢失的问题,有两种可靠的解决思路:手动暂存待执行操作,或者结合SQLite的内置重试机制优化流程。
一、手动暂存操作队列(推荐)
核心思路是把执行失败的SQL操作(语句+参数)暂存到一个队列里,每次循环先尝试清空队列里的积压操作,再执行新的操作,确保锁定解除后所有未提交的数据都能被补上。
修改后的示例代码如下:
import sqlite3 import traceback import time from collections import deque # 初始化数据库连接,可设置超时时间(默认5秒) con = sqlite3.connect("test.db", timeout=10) cur = con.cursor() # 创建暂存队列,存储失败的(语句, 参数) pending_ops = deque() i = 0 while True: # 先处理积压的待执行操作 while pending_ops: stmt, params = pending_ops[0] try: cur.execute(stmt, params) con.commit() print(f"补提交: {params[0]}") pending_ops.popleft() except sqlite3.OperationalError as e: if "database is locked" in str(e): # 仍处于锁定状态,跳出循环等下一轮重试 break else: # 处理其他SQL错误 traceback.print_exception(e) pending_ops.popleft() # 处理当前新操作 try: cur.execute("INSERT INTO TestTable VALUES(?)", (i,)) con.commit() print(f"已提交 i={i}") except sqlite3.OperationalError as e: if "database is locked" in str(e): # 锁定状态,将操作加入暂存队列 pending_ops.append(("INSERT INTO TestTable VALUES(?)", (i,))) print(f"暂存 i={i},等待解锁后提交") else: traceback.print_exception(e) i += 1 print(f"i={i}") time.sleep(10)
这个方案的优势:
- 所有失败的操作都会被保留,不会丢失
- 每次循环优先处理积压操作,确保数据最终能写入数据库
- 逻辑清晰,可根据需求扩展(比如设置队列上限、将队列持久化到文件防止程序崩溃丢失数据)
二、利用SQLite的内置超时重试
SQLite的connect方法自带timeout参数,默认值为5秒,意思是遇到数据库锁定时,会等待指定时长后重试,而非立刻抛出错误。你可以适当调大这个值,比如设置为30秒:
con = sqlite3.connect("test.db", timeout=30)
但这个方法仅能解决短时间锁定问题,如果DBBrowser长时间占用数据库,仍会触发错误,建议和手动暂存队列结合使用,实现双重保障。
注意事项
- 仅在SQL执行成功后调用
commit,避免无效提交 - 如果程序存在意外崩溃的可能,建议将暂存队列持久化到本地文件(比如用JSON格式存储),重启后可恢复未提交的操作
- 保持一个长期的数据库连接,避免循环中频繁创建新连接或游标
内容的提问来源于stack exchange,提问作者Fenekhu
相关产品推荐
相关产品推荐

