最全面的wcdb入門和總結(jié)
開篇前的思考:一個(gè)優(yōu)秀的數(shù)據(jù)庫(kù)需要具備什么樣的特性?
第一:高效。高效的增刪查改是數(shù)據(jù)庫(kù)最基本的要求
第二:易用。一個(gè)簡(jiǎn)單的數(shù)據(jù)庫(kù)操作,我們不需要做什么額外的初始化、操作等,組件能統(tǒng)一完成這些任務(wù)
第三:完整。優(yōu)秀的數(shù)據(jù)庫(kù)能完整的覆蓋各種極端場(chǎng)景,包括數(shù)據(jù)庫(kù)損壞、監(jiān)控統(tǒng)計(jì)、復(fù)雜的查詢,優(yōu)秀的日志系統(tǒng)等
WCDB接入與遷移
第一步:引入依賴

第二步:選擇接入的架構(gòu)

第三步:原有sqlcipher的遷移
android.database.* -> com.tencent.wcdb.*
android.database.sqlite.* -> com.tencent.wcdb.database.*
第四步:原有SQLCipher遷移(從加密數(shù)據(jù)庫(kù)往wcdb遷移)

WCDB For Android
1 確定的sqlite版本
在 Android SDK 中,SQLite 是會(huì)不斷升級(jí)的,實(shí)際上使用哪個(gè)版本的 SQLite 取決于 APP 運(yùn)行在哪個(gè)版本的系統(tǒng)上,這是對(duì)開發(fā)者來(lái)說(shuō)相當(dāng)不友好,因?yàn)橥瑯拥?SQL 語(yǔ)句會(huì)有不同的性能表現(xiàn)。如果業(yè)務(wù)需要使用 SQLite 的新特性就更加需要確定版本的 SQLite 來(lái)保證新特性在所有手機(jī)上都可用。
WCDB 由于內(nèi)建了自己的 SQLite 實(shí)現(xiàn)(準(zhǔn)確來(lái)說(shuō)是 SQLCipher),所以 SQLite 版本是確定的,這規(guī)避了很多開發(fā)上的問(wèn)題。
2 Cursor 實(shí)現(xiàn)優(yōu)化
在實(shí)際使用的很多的場(chǎng)景中,我們都需要去數(shù)據(jù)庫(kù)執(zhí)行查詢數(shù)據(jù),得到cursor,然后通過(guò)cursor,封裝數(shù)據(jù)到我們的list中。這個(gè)查詢過(guò)程中,數(shù)據(jù)會(huì)緩存到到cursorWindow中,也即是(SQLite → CursorWindow → Java)
Wcdb的優(yōu)化是,直接切斷cursorWindow,通過(guò)SQLiteDirectCursor,將SQLite直接返回到j(luò)ava中。在大部分不需要將 Cursor 傳遞出去的場(chǎng)景,能很好的解決 Cursor 的額外消耗,特別是結(jié)果集大于 2MB 的場(chǎng)合
3 mmicu與動(dòng)態(tài)icu加載
什么是mmicu?wcdb自帶的分詞器,與官方的icu不同的是,wcdb對(duì)中文分詞以及icu庫(kù)加載做了特殊處理。官方的icu庫(kù)會(huì)因?yàn)椴煌腶ndroid系統(tǒng),附帶不同的中文詞庫(kù),會(huì)帶來(lái)不同的分詞效果。 wcdb這樣做,也即是相當(dāng)于統(tǒng)一了各個(gè)版本的分詞效果
動(dòng)態(tài)icu加載:官方ICU 還有一個(gè)嚴(yán)重的問(wèn)題是動(dòng)態(tài)庫(kù)和自帶的數(shù)據(jù)文件體積很大,超過(guò) 10MB,WCDB 做了一個(gè)兼容層 icucompat,通過(guò)系統(tǒng)帶的數(shù)據(jù)文件推斷 ICU 版本, 通過(guò) dlopen 動(dòng)態(tài)加載不同的符號(hào)名稱,然后通過(guò)宏來(lái)模擬直接調(diào)用方便開發(fā)。最終實(shí)現(xiàn)效果便是在不需要自帶 ICU 庫(kù)的前提下使用 ICU 庫(kù)的斷詞、歸一化等功能,為最終 APK 包省下 10MB 以上空間。
WCDB的性能


從上面可以看出wcdb對(duì)標(biāo)與sqlcipher,在數(shù)據(jù)庫(kù)的插入、查詢、更新方面的性能是有很大的提升的
方案1:官方Dump方案

流程分析
dump方案的原理很簡(jiǎn)單,主要
思路是讀取db中的sqlite_master表,里面保存著所有的索引、表名、建表語(yǔ)句等。
步驟1:讀取create語(yǔ)句,建立新表
步驟2:執(zhí)行select * From語(yǔ)句,每讀出一行,就輸出一個(gè)insert語(yǔ)句
經(jīng)過(guò)步驟2,整個(gè)db就已經(jīng)dump出來(lái)了
備注:忽略掉損壞錯(cuò)誤,繼續(xù)遍歷下個(gè)表,最終可以把所有沒損壞的表以及損壞了的表的前半部分讀取出來(lái)
思考:官方的方案存在什么問(wèn)題?
1 耗費(fèi)空間。先要保存dump 出來(lái)的SQL的空間,這個(gè) 大概一倍DB大小,還要另外一倍 DB大小來(lái)新建DB恢復(fù)。
2 成功率低,sqlite_master讀取失敗,特別是第一頁(yè)損壞,會(huì)導(dǎo)致后續(xù)所有內(nèi)容無(wú)法讀出
3 花費(fèi)時(shí)間長(zhǎng)(后面有對(duì)比圖)
方案2 BackupKit 備份方案(優(yōu)化版Dump + 壓縮)
微信在原先的官方dump+gzip方案上做出了兩點(diǎn)改動(dòng):
1使用了自定義 的二進(jìn)制格式承載Dump輸出。恢復(fù)的時(shí)候不需要重復(fù)的編譯SQL語(yǔ)句,編譯一次就可以 插入整個(gè)表的數(shù)據(jù)了,恢復(fù)性能也有一定提升
2壓縮操作則放到別的線程同時(shí)進(jìn)行,在雙核以上的環(huán)境 基本可以做到無(wú)額外時(shí)間消耗。
優(yōu)化前的對(duì)比:

優(yōu)化后備份方案和原來(lái)的dump方案的對(duì)比

方案3:Repair Kit
核心思想:備份sqlite_master表
1 如何判斷備份時(shí)機(jī)?輪詢
2備份文件也會(huì)損壞,怎么辦?
雙備份 的機(jī)制。具體來(lái)說(shuō)就是會(huì)有新舊兩個(gè)備份文件,每個(gè)文件頭都加上 CRC 校驗(yàn);每次備份時(shí),從兩個(gè)備份文件中選出一個(gè)進(jìn)行覆蓋。具體怎么選呢??jī)?yōu)先選損壞那個(gè)備份文件,如果兩個(gè)都有效,那么就選相對(duì)較舊的。這就保證了即使本次寫入導(dǎo)致文件損壞,還有另外一份備份可以用

WCDB 的 WAL和異步checkPoint
原子性提交和回滾操作 的默認(rèn)方法是 rollback journal,其工作模式如下圖所示

如以上流程圖所示,當(dāng)我們需要對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作的時(shí)候,sqlite會(huì)把需要修改的部分copy到j(luò)ournal,以防出現(xiàn)意外的時(shí)候,可以進(jìn)行回滾。當(dāng)寫入完成,用戶提交事務(wù)后,sqlite將會(huì)清空journal,完成一個(gè)完整的寫事務(wù)
問(wèn)題是什么?
Rollback 模式中,每次拷貝原始內(nèi)容或?qū)懭胄聝?nèi)容后,都需要確保之前寫入的數(shù)據(jù)真正寫入到磁盤,而不是緩存在操作系統(tǒng)中,這需要發(fā)起一次 fsync 操作,通知并等待操作系統(tǒng)將緩存真正寫入磁盤,這個(gè)過(guò)程十分耗時(shí)。除了耗時(shí)的 fsync 操作,寫入 -journal 以及 DB 主文件的時(shí)候,是需要獨(dú)占整個(gè) DB 的,否則別的線程/進(jìn)程可能讀取到寫到一半的內(nèi)容。這樣的設(shè)計(jì)使得寫操作與讀操作是互斥的,并發(fā)性很差
WCDB WAl模式的優(yōu)越性體現(xiàn)在哪?

從上圖可以看出,wal模式下,寫數(shù)據(jù)并不是直接寫到-wal文件末尾。讀操作的時(shí)候,將結(jié)合 DB 主文件以及 -wal 的內(nèi)容返回結(jié)果。由于讀操作只讀取 DB 主文件和 -wal 前面沒在寫的部分,不需要讀取寫操作正在寫到一半的內(nèi)容,WAL 模式下讀與寫操作的并發(fā)由此實(shí)現(xiàn)寫操作需要執(zhí)行Checkpoint才可以把當(dāng)前wal文件中的內(nèi)容合并到db主文件中。由于寫操作將內(nèi)容臨時(shí)寫到 -wal 文件,-wal 文件會(huì)不斷增大且拖慢讀操作,因此需要定期進(jìn)行Checkpoint 操作將 -wal 文件保持在合理的大小。
性能如何?看看效果
寫性能對(duì)比:

讀性能對(duì)比

以上就是wcdb wal模式的對(duì)比,讀性能則如官方文檔所說(shuō),WAL 模式單線程性能要稍稍差于 Rollback 模式,但由于 WAL 模式支持讀寫并發(fā),WCDB 也開啟了線程池,因此 WAL 模式的并發(fā)性要遠(yuǎn)遠(yuǎn)好于 Rollback 模式。
參考:
https://cloud.tencent.com/developer/article/1031030
https://cloud.tencent.com/developer/article/1005623
https://cloud.tencent.com/developer/article/1005534
https://cloud.tencent.com/developer/article/1005513