科技 · 實用指南
Python 顯示「database is locked」?先核對錯誤碼,再選擇正確重試範圍
延長逾時不能更新過期的 WAL 快照。先記錄錯誤碼、失敗階段和 Python 交易模式,再決定重試什麼。

同樣的 database is locked 提示,可能對應不同復原工作:等待其他寫入連線、捨棄過期讀取快照,或完成尚未成功的提交。盲目重複最後一條 UPDATE,可能持續使用過期判斷,也可能把寫入執行兩次。
從拋出例外的連線開始。記錄 Python 與實際 SQLite 版本、資料庫身分、日誌模式、交易控制設定,以及失敗發生在 BEGIN、UPDATE 還是 COMMIT。改變應用程式交易策略前,先用臨時、可捨棄的資料庫重現。
保留擴充錯誤碼與失敗階段
Python 3.11 起,源自 SQLite 的例外具有 sqlite_errorcode 和 sqlite_errorname。用 getattr(exc, "sqlite_errorcode", None) 與 getattr(exc, "sqlite_errorname", None) 記錄它們;先保留完整值,再用 code & 255 求主要類別。SQLITE_BUSY 為 5,SQLITE_LOCKED 為 6,SQLITE_BUSY_SNAPSHOT 為 517。
BUSY 通常涉及其他連線;LOCKED 可涉及同一連線未完成的陳述式或共用快取。隔離實驗中,SELECT 游標只讀取部分結果且未關閉,此時 DROP TABLE 回傳 database table is locked、錯誤碼 6。完成預定讀取後結束或關閉實際游標,不要僅憑錯誤文字終止無關程序。
確認誰控制交易邊界
Python 3.12 引入 autocommit。autocommit=True 時,即使透過 SQL BEGIN 開啟交易,Connection.commit() 和 rollback() 也沒有效果;該交易應使用明確 SQL COMMIT 或 ROLLBACK,並檢查 in_transaction。autocommit=False 時,提交和回復會立即開啟另一筆 DEFERRED 交易。isolation_level 只控制 LEGACY_TRANSACTION_CONTROL。
不要把切換模式當成活動交易的修復捷徑:把 autocommit 改為 True 會提交待處理工作。框架也可能負責這些邊界。目前 Python 的連線內容管理器會在提交失敗後回復,但 autocommit=True 時不做交易處理,而且不會關閉連線。已關閉的 2016—2021 年 CPython 缺陷是歷史證據,不能據此說今天仍存在同一問題。
增加等待前,先選擇重試範圍
BEGIN IMMEDIATE 在操作尚未開始時回傳 BUSY,可在有限重試預算內重新取得寫入資格。縮短交易,持有交易時避免網路呼叫。BEGIN IMMEDIATE 成功,不等於任何日誌模式下後續每項操作都保證不會 BUSY。
WAL 讀取交易在另一連線提交後回傳 517 時,應結束該交易、開啟新交易、重新讀取並重新計算判斷。等待或只重試 UPDATE 無法刷新舊快照。在回復日誌模式中,COMMIT 被其他讀取者阻擋而回傳 BUSY 時,交易可能仍保持活動;確認文件所述條件解除後,重試 COMMIT,不要重複已經套用的寫入。
臨時雙連線實驗實際觀察到什麼
我們用 Python 3.12.14、實際 SQLite 3.53.1、timeout=0.05 秒、臨時資料庫檔案和明確 SQL 邊界執行了四個小實驗,沒有使用使用者資料庫。WAL 模式中,連線 A 讀到計數 10,B 改為 11,A 隨後寫入回傳 517。autocommit=True 下,A.rollback() 後 in_transaction 仍為 True。SQL ROLLBACK 將其清除;新的 BEGIN IMMEDIATE 重新讀到 11,一次增加後提交為 12。
第一條連線占有寫入資格時,第二條連線的 BEGIN IMMEDIATE 回傳錯誤碼 5,失敗啟動者沒有活動交易。另一個 DELETE 日誌實驗中,開放讀取交易阻擋另一連線的 COMMIT:錯誤碼 5,寫入交易仍活動。結束讀取交易後,只重試 COMMIT,初始 10 最終變為 11。這些是可重現的合成情境,不是對未知應用程式的診斷,也不證明正式環境安全。
以有上限、可觀察的復原流程收尾
連線逾時是等待預算,不能修復過期快照或死結。等待無法解除鎖循環時,SQLite 可能不呼叫 busy handler。記錄次數、耗時、錯誤碼、階段與交易狀態;設定重試上限,預算耗盡後明確報告失敗。不要捕捉所有 OperationalError 並一律當成可重試競爭。
確認修復前,以另一連線核對預期持久值,確認目標交易結束,並重複相關競爭情境。autocommit=False 下,新開的空交易可能讓 in_transaction 繼續為 True,應結合模式解釋。不要刪除 WAL 或共用記憶體檔案來「解鎖」。若出現損毀、I/O 錯誤或交易歸屬不明,應停止此重試流程,調查對應原因。
