2011-04-20 32 views
4

我正在編寫一個使用由fmdatabase包裝的sqlite3的iOS應用程序。我遇到了這樣的問題:在某些時候,我的程序停留在FMDatabase庫的一個循環中,特別是一個調用sqlite3_step並發現數據庫正忙的函數,然後一遍又一遍地重試。如何調試鎖定的sqlite3數據庫

我正在尋找一般的調試工具和技巧,因爲這將是太多,給我在這裏的整個設置。有些事情可能會造成影響,我打開了一個數據庫句柄來處理已經在另一個線程中有句柄的數據庫。 sqlite3_threadsafe()返回2,所以我知道它的啓用。我也通過製作一個非常簡單的選擇和更新語句來測試這個新的連接。當我讓我的程序運行,並嘗試運行數據庫更新時,我卡住了。

我的程序自己創建的update語句沒有錯,因爲當我不打開兩個連接時,這個查詢運行正常。然而,我沒有看到我可能會出錯的地方...

任何幫助或提示,我可能會錯的地方將不勝感激。

+0

您是否提交了更新?另請參閱[解鎖通知方案](http://www.sqlite.org/unlock_notify.html) – Benoit 2011-04-20 15:32:16

+0

fmdatabase調用sqlite3_finalize但不提交。我承諾它,但似乎沒有任何區別 – Ying 2011-04-20 16:35:34

回答

21

SQLite在寫入操作期間(即,在任何表上發生寫入,其他寫入,同時任何地方的任何表都不會發生)時,SQLite鎖定整個數據庫。某些數據庫通過表級鎖提供併發寫入,或者有時通過行級鎖提供併發寫入。爲了與SQLite的實現形成鮮明對比,表級鎖定基本上意味着當您將數據寫入給定的表時,其他線程不能同時寫入該表中的任何記錄(但是,寫入其他表可以在某些情況下同時發生)。同樣,行級鎖進一步強化了它,並且只允許涉及的必要行被鎖定,允許從多個線程同時寫入同一個表。這裏的想法是最大限度地減少寫入操作需要鎖定的數據量,這可以有效地增加數據庫中可能的併發寫入數量,並且取決於您的實現/如何使用數據庫,這可以顯着增加吞吐量。

現在,回到你的問題......

說SQLite是線程安全的,並不意味着多個線程可以同時寫它的事實 - 這意味着它從多個線程處理訪問的方式 - (a)允許超時/重試,以及(b)當數據庫當前擁有一個鎖時,返回一個有用的錯誤(SQLITE:Busy)。也就是說,線程安全只不過意味着「多線程可以以不會因同時訪問而導致數據損壞的方式訪問此數據。」

基本上,在代碼中的某個地方,一個線程試圖在另一個線程釋放其對數據庫的鎖之前進行更新。這是SQLite常見的障礙,因爲作者/文檔會告訴你SQLite可以像冠軍一樣處理併發。現實情況是,SQLite認爲「併發性支持」等於試圖非常快速地使數據庫上的鎖只能保持很短的時間,因此在超時之前釋放數據庫上的鎖。在很多情況下,這很好,永遠不會妨礙你。但是,擁有非常短暫的鎖與實際上允許從多個線程併發寫入的方式不一樣

想象一下,它像iOS的多任務處理方式(至少在我寫這篇文章的時候是iOS 5) - 真的是讓其他應用程序暫停並返回給它們。這具有以下效果:(a)由於CPU利用率較低,電池壽命更好;(b)每次啓動時都不必從頭開始創建應用程序。這很棒,但在iOS中使用的實際單詞「多任務」在技術上並不意味着與其他環境(甚至是Mac OS X)中的「多任務」相同。

SQLite的方式是一樣的。他們有「併發」支持嗎?好吧,但他們定義單詞「併發性」的方式並不是數據庫世界其餘部分定義「併發性」的方式。

沒有人真的是錯的,但在這樣的情況下,它增加了執行混亂。

+0

是的,我不得不保護數據庫與我自己的鎖,我試圖避免,但不能... – Ying 2011-04-27 15:33:49

+0

這一定會做到這一點。 – jefflunt 2011-04-27 21:50:26

+2

現在他們有了一種叫做WAL的東西。 – Alex 2013-01-17 21:17:58