原理:文件數據庫sqlite,同一時刻允許多個進程/線程讀,但同一時刻只允許一個線程寫。在操行寫操作時,數據庫文件被瑣定,此時任何其他讀/寫操作都被阻塞,如果阻塞超過5秒鍾(默認是5秒,能過重新編譯sqlite可以修改超時時間),就報"database is locked"錯誤。
所以,在操作sqlite時,應該即時關閉連接;打開連接后,盡量減少非常費時的操作。
多線程同時訪問數據庫時,報數據庫鎖定的問題,錯誤信息是:
Unknown error finalizing or resetting statement (5: database is locked)
最后通過FMDatabaseQueue解決了這個問題,本文總結一下:
FMDatabase 不能多線程使用一個實例
多線程訪問數據庫,不能使用同一個FMDatabase 的實例。否則會發生異常。如果線程使用單獨的FMDatabase 實例是允許的,但是同樣有可能發生database is locked的問題。這是由於多線程對sqlite的競爭引起的
FMDatabaseQueue 解決這個問題的思路是:創建一個隊列,然后將放入的隊列的block順序執行,這樣避免了多線程同時訪問數據庫
解決方案 共享同一個FMDatabaseQueue實例
創建一個DBHelper 類
使用FMDatabaseQueue之后,管理db
原本使用FMDatabase類,需要手工調用db的open和close方法
但是用FMDatabaseQueue,不需要調用open,因為查看代碼發現,Queue已經open了。至於要不要close,我也不確定,因為官方的sample code沒有調用close。實際應用中,我也沒有調用,好像沒有問題。
所以,使用Queue,是不需要自己打開和關閉db的。但是如果使用了FMResultSet,rs倒是需要關閉,否則會報warning:
if ([db hasOpenResultSets]) {
NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]");
為了不看到warning,我都在block里調用了[rs close]
刷新數據庫文件路徑
具體到我們的應用,還有一個特殊問題需要考慮。因為我們的APP可以切換賬戶,而賬戶的db文件是獨立的。所以當用戶重新登錄的時候,需要刷新一下Helper的queue
如果不這么做,由於Helper是單例,那么切換賬戶以后,用戶B訪問的還是用戶A的數據庫。刷新的調用,一般放在登錄之后,進入主頁面之前就可以了