前言
移動端的數據庫選型一直是一個難題,直到前段時間看到了WeMobileDev(微信前端團隊)放出了第三個開源組件-WCDB
WCDB(WeChat DataBase)是微信官方的移動端數據庫組件,致力于提供一個高效、易用、完整的移動端存儲方案
微信團隊怎么說
基于SQLCipher
WCDB-iOS/Mac
WCDB-Android
數據庫損壞修復工具WDBRepair
背景
WCDB的出現可以說解決了目前移動端數據庫的幾個難點
- 首先在選型上,FMDB的SQL拼接、難以防止的SQL注入;CoreData雖然可以方便ORM,但學習成本高,穩定性堪憂,而且多線程雞肋;另外基于C語言的sqlite我想用的人也應該不多;除了上述關系型數據庫之外然后還有一些其他的Key-Value型數據庫,如我用過的Realm,對于ObjC開發者來說,上手倒是沒什么難度,但缺點顯而易見,需要繼承,入侵性強,對于單繼承的OC來說這并不理想,而且對于集合類型不完全支持,復雜查詢也比較無力。
-
高效
多線程高并發:WCDB支持多線程讀與讀、讀與寫并發執行,寫與寫串行執行。
-
批量寫操作性能測試:
批量寫 ops/sec WCDB 458000 FMDB 161000
-
易用 WCDB支持一句代碼即可將數據取出并組合為object
WINQ(WCDB語言集成查詢):通過WINQ,開發者無須為了拼接SQL的字符串而寫一大坨膠水代碼。
ORM(Object Relational Mapping):WCDB支持靈活、易用的ORM。開發者可以很便捷地定義表、索引、約束,并進行增刪改查操作。
-
像這樣
[database getObjectsOfClass:WCTSampleConvenient.class fromTable:tableName where:WCTSampleConvenient.intValue>=10 limit:20];
-
完整
- 加密:WCDB提供基于SQLCipher的數據庫加密。
- 損壞修復:WCDB內建了Repair Kit用于修復損壞的數據庫。
- WCDB提供接口直接獲取SQL的執行耗時,可用于監控性能。
- 反注入:WCDB內建了對SQL注入的保護
ORM
在WCDB內,ORM(Object Relational Mapping)是指
將一個ObjC的類,映射到數據庫的表和索引;
將類的property,映射到數據庫表的字段;
這一過程。通過ORM,可以達到直接通過Object進行數據庫操作,省去拼裝過程的目的。
WCDB通過內建的宏實現ORM的功能。如下
PS:但我不建議這么做,首先要避免在.h文件中引用<WCDB/WCDB.h>,因為你一旦引用,就需要改變.m文件為.mm文件,因為WCDB是基于objectiveC++;你可以使用Category特性將其隔離,在category中引用<WCDB/WCDB.h>,并遵守WCTTableCoding協議,使用WCDB_PROPERTY將聲明綁定到數據庫表的字段。然后在模型類中引用category。達到不印象Controller和View的目的。這點官方wiki中也有提到,使用文件模板來創建。具體請見Demo
對于一個已有的ObjC類,
引用WCDB框架頭文件#import <WCDB/WCDB.h>,并定義類遵循WCTTableCoding協議
WCDB_PROPERTY用于在頭文件中聲明綁定到數據庫表的字段。
WCDB_IMPLEMENTATION,用于在類文件中定義綁定到數據庫表的類。同時,該宏內實現了WCTTableCoding。因此,開發者無須添加更多的代碼來完成WCTTableCoding的接口
WCDB_SYNTHESIZE,用于在類文件中定義綁定到數據庫表的字段。
WCDB_PRIMARY用于定義主鍵
WCDB_PRIMARY_AUTO_INCREMENT 用于定義自增主鍵
WCDB_INDEX用于定義索引
WCDB_UNIQUE用于定義唯一約束
WCDB_NOT_NULL用于定義非空約束
CRUD
得益于ORM的定義,WCDB可以直接進行通過object進行增刪改查(CRUD)操作。
-
增
//插入 Person *man = [[Person alloc] init]; man.isAutoIncrement = YES; man.name = @"Hello, WCDB!"; man.age = 12; return [database insertObject:man into:TABLE_WCDB_NAME];
-
刪
return [database deleteObjectsFromTable:TABLE_WCDB_NAME where:Person.studentId == studentId];
-
改
Person *person = [[Person alloc] init]; person.name = content; return [database updateRowsInTable:TABLE_WCDB_NAME onProperties:Person.name withObject:person where:Person.studentId == studentId];
-
查
NSArray<Person *> * person = [database getObjectsOfClass:Person.class fromTable:TABLE_WCDB_NAME orderBy:Person.localID.order()];
Transaction
WCDB內可通過兩種方式執行Transaction(事務),一是runTransaction:接口
這種方式要求數據庫操作在一個BLOCK內完成,簡單易用。
另一種方式則是獲取WCTTransaction對象
WCTTransaction對象可以在類或函數間傳遞,因此這種方式也更具靈活性。
WINQ
WINQ(WCDB Integrated Query,音'wink'),即WCDB集成查詢,是將自然查詢的SQL集成到WCDB框架中的技術,基于C++實現。
- 免去拼接SQL字符串、防注入
- 借助IDE代碼提示和編譯器語法檢查
- 對于一個已綁定ORM的類,可以通過className.propertyName的方式,獲得數據庫內字段的映射
- WINQ的接口包括但不限于:
- 一元操作符:+、-、!等
- 二元操作符:||、&&、+、-、*、/、|、&、<<、>>、<、<=、==、!=、>、>=等
- 范圍比較:IN、BETWEEN等
- 字符串匹配:LIKE、GLOB、MATCH、REGEXP等
- 聚合函數:AVG、COUNT、MAX、MIN、SUM等
- ...
原理
- 初衷,適應WCDB+ORM解決SQL字符串的代碼冗余和難以被編譯器進行語法檢查而造成的錯誤和時間浪費。SQL字符串太容易被注入
- SQL抽象
- 封裝常用操作,覆蓋80%的使用場景
- 暴露底層接口,適配剩余20%的特殊情況
- 定義常用操作
- 特殊場景所暴露的底層接口,應該以什么形式存在?
- SELECT、DISTINCT、ALL等等大寫字母是keyword,屬于SQL的保留字。
- result-column、``table-or-subquery、expr等等小寫字母是token。token可以再進一步地展開其構成的語法規則。
- 將固定的keyword,封裝為函數名,作為連接。
- 將可以展開的token,封裝為類,并在類內實現其不同的組合。
- 在語法規則中,WHERE、LIMIT等都接受expr作為參數。因此,不管SQL多么復雜,StatementSelect也只接受Expr的參數。而其組合的能力,則在Expr類內實現。
數據庫修復
- 官方的Dump恢復方案
- 遍歷sqlite_master表,將未損壞的表和已損壞的前半部分讀取出來將dump 出來的SQL語句逐行執行,最終可以得到一個等效的新DB
功率約為30%。 - 第一頁就損壞后續無法讀取
- 遍歷sqlite_master表,將未損壞的表和已損壞的前半部分讀取出來將dump 出來的SQL語句逐行執行,最終可以得到一個等效的新DB
- 備份恢復方案
- COPY
- 在DB完好的時候執行.dump
- Backup API: SQLite自身提供的一套備份機制,按 Page 為單位復制到新 DB, 支持熱備份。
- 最終選擇Dump + 壓縮,恢復成功率達到72%
- 解析B-tree恢復方案(RepairKit)
- 成功率約為78%
- 不同方案的組合
- RepairKit 嘗試恢復最新數據
- 備份恢復 遇到錯誤填補漏缺
- Dump 最后的嘗試
For Android
-
基本功能
- 基于SQLCipher的數據庫加密
- 使用連接池實現并發讀寫
- 內建 Repair Kit 可用于修復損壞數據
- 針對占用空間大小優化的數據庫備份/恢復功能
- 日志輸出重定向以及性能跟蹤接口
- 內建用于全文搜索的 mmicu FTS3/4 分詞器
-
接入與遷移
- WCDB for Android 可通過 Maven 或 AAR 包引用,API 接口與 Android SDK 非常相近, 所以將已有的 App 遷移到 WCDB 是相當容易的。
- Android 接入與遷移
-
數據庫修復
-
從源碼編譯
- 你可以使用預編譯的依賴庫(OpenSSL crypto 和 SQLCipher)來編譯 WCDB for Android, 使用 Gradle 或 Android Studio 皆可。Android Studio 請導入 android 目錄作為 Root Project。
- 編譯 WCDB 需要安裝 Android NDK r11c 或以上,并在 android/local.properties 上配置好 SDK 與 NDK 路徑。Android Studio 一般會幫你配置好。
- 如果你需要自己編譯 OpenSSL 等依賴項,你需要一個 Bash 環境(Windows 可以安裝 Cygwin 或 MSys)、target 為本機的 C 編譯器(如 GCC)、Perl 5 以及 Tcl。之后執行下面命令即可編譯依賴項。
參考資料