1. 程式人生 > >使用WCDB中踩過的坑

使用WCDB中踩過的坑

使用WCDB中踩過的坑

一.遇到的問題

  • 在版本覆蓋安裝時,發現有些資料丟失;後通過抓包檢視沙盒資料庫檔案時,發現WCDB資料庫某張表損壞,造成資料丟失。image

二.調查問題過程

  1. 檢視問題出現的版本4.7.6,首次安裝此版本,安裝時會先執行,內建資料庫會被拷貝到沙盒中,由於此時有資料更新,會拉取介面,更新資料庫檔案,發現所有快取資料沒有問題;
  2. 首次安裝上一個版本4.7.0,此時一樣會先執行,內建資料庫會被拷貝到沙盒中,由於此時有資料更新,會拉取介面,更新資料庫檔案,發現4.7.0版本,所有快取資料也沒有問題;
  3. 在第二步基礎上,用4.7.6版本,覆蓋安裝4.7.0版本,此時,app內的執行順序是:刪除本地原有的資料庫,把內建資料庫會被拷貝到沙盒中,替換4.7.0版本的資料庫檔案,此時發現有資料更新,會拉取介面,更新資料庫檔案,問題出現了,WCDB中某張表損壞了,資料丟失。此時列印的日誌如下:和查詢表時提示一致;image
  4. 對比4.7.0版本和4.7.6版本程式碼,發現4.7.6版本的某張表裡資料的儲存格式和4.7.0有區別;在執行內建資料庫會被拷貝到沙盒中前,只刪除了Cache.db檔案,而Cache.db-wal和Cache.db-shm檔案沒有被刪除,此時拉取介面更新資料庫時,更新到Cache.db-wal,再快取到Cache.db時,由於格式不一致,造成表損壞。

三.SQLite中的wal和shm

  • wal全稱Write Ahead Log 即預寫日誌,shm是日誌索引檔案;當每個事務對資料庫執行修改時,會將修改後的日誌存入到wal檔案中,當事務讀取資料庫資料時,優先讀取wal檔案中讀取,讀不到再從資料庫中讀取。
  • 如何做到讀寫併發操作???首先事務在寫入時,是將新資料寫入到日誌檔案中,不更新db,當事務提交時,也不影響db,只是將日誌持久化了。這樣看,最新的資料是在日誌中的,在事務讀取時,首先在日誌檔案中,找到最近的一個end-mark(事務提交節點),在這個節點之前查詢對應的資料。如果找不到,再去db中查詢,這樣看在極端的情況下,每次查詢都要在wal檔案和db中各查詢一次,影響效能。為了提高效能,wal-index檔案(也就是.shm檔案)用來記錄page號和該page在wal檔案中的偏移,並且wal-index檔案採用共享記憶體,避免了對整個wal檔案的查詢(共享記憶體也有一個弊端就是SQlite的程序不能跨機器)。
  • wal檔案由32位元組的首部和若干個幀組成,每個幀由24位元組幀首部和一頁資料組成。

四.總結

  1. 刪除本地Cache.db檔案時,要同時刪除Cache.db-wal和Cache.db-shm檔案;否則會出現,由於前後資料格式不一致時,造成資料庫表損壞情況;
  2. 檢測資料庫損壞,做資料庫修復或者刪除重建庫處理。