榮登Github日榜!微信最新開源MMKV

騰訊開源發表於2018-09-26

MMKV 開源當日即登Github Trending日榜,三日後榮登周榜。MMKV 在騰訊內部開源半年,得到公司內部團隊的廣泛應用和一致好評。

MMKV 是基於 mmap 記憶體對映的移動端通用 key-value 元件,底層序列化/反序列化使用 protobuf 實現,效能高,穩定性強。從 2015 年中至今,在 iOS 微信上使用已有近 3 年,其效能和穩定性經過了時間的驗證。近期已移植到 Android 平臺,移動端全平臺通用,並全部在 Github 上開源。

MMKV源起
在微信客戶端的日常運營中,時不時就會爆發特殊文字引起系統的 crash,參考文章:《iOS微信特殊字元保護方案》,文章裡面設計的技術方案是在關鍵程式碼前後進行計數器的加減,通過檢查計數器的異常,來發現引起閃退的異常文字。在會話列表、會話介面等有大量 cell 的地方,希望新加的計時器不會影響滑動效能;另外這些計數器還要永久儲存下來——因為閃退隨時可能發生。這就需要一個效能非常高的通用 key-value 儲存元件,我們考察了 SharedPreferences、NSUserDefaults、SQLite 等常見元件,發現都沒能滿足如此苛刻的效能要求。考慮到這個防 crash 方案最主要的訴求還是實時寫入,而 mmap 記憶體對映檔案剛好滿足這種需求,我們嘗試通過它來實現一套 key-value 元件。

MMKV原理
• 記憶體準備 通過 mmap 記憶體對映檔案,提供一段可供隨時寫入的記憶體塊,App 只管往裡面寫資料,由作業系統負責將記憶體回寫到檔案,不必擔心 crash 導致資料丟失。資料組織 資料序列化方面我們選用 protobuf 協議,pb 在效能和空間佔用上都有不錯的表現。
• 寫入優化 考慮到主要使用場景是頻繁地進行寫入更新,我們需要有增量更新的能力。我們考慮將增量 kv 物件序列化後,append 到記憶體末尾。
• 空間增長 使用 append 實現增量更新帶來了一個新的問題,就是不斷 append 的話,檔案大小會增長得不可控。我們需要在效能和空間上做個折中。

更詳細的設計原理參考 GitHub Wiki 文件。

MMKV功能
MMKV 優點
• 高效
MMKV 使用 mmap 來保證記憶體與檔案的同步, 底層使用 protobuf 來儲存資料, 深入挖掘了作業系統的潛力來達到最高的效能。
• 易用
你可以像使用 Dictionary 一樣來使用 MMKV, 無需任何配置。所有變更馬上生效, 無需呼叫任何 sync 之類的同步介面。
• 小巧
o 只有十幾個檔案: MMKV 包含了序列化/反序列化幫助類和 mmap 邏輯,再無其他累贅邏輯。程式碼非常精簡。
o 二進位制少於 60K: MMKV 每個架構只加了不到 60K 的二進位制大小(解壓後)。一般 App 都會壓縮打包釋出,實際上上會比 60K 更小。
MMKV for Android 特有功能
我們在遷移到 Android 的過程中,不是簡簡單單地照搬 iOS 的實現,深入分析了 Android 平臺現有 kv 元件的痛點,在原有功能基礎上,開發了 Android 特有的功能。

• 多程式訪問
通過與 Android 開發同學的溝通,瞭解到系統自帶的 SharedPreferences 對多程式的支援不好。現有基於 ContentProvider 封裝的實現,雖然多程式是支援了,但是效能低下,經常導致 ANR。考慮到 mmap 共享記憶體本質上的多程式共享的,我們在這個基礎上,深入挖掘了 Android 系統的能力,提供了可能是業界最高效的多程式資料共享元件。具體實現原理可以前往 GitHub 檢視原始碼和 wiki 文件。

• 匿名記憶體
在多程式共享的基礎上,考慮到某些敏感資料(例如密碼)需要程式間共享,但是不方便落地儲存到檔案上,直接用 mmap 不合適。我們瞭解到 Android 系統提供了 Ashmem 匿名共享記憶體的能力,發現它在程式退出後就會消失,不會落地到檔案上,非常適合這個場景。我們很愉快地提供了 Ashmem MMKV 的功能。

• 資料加密
不像 iOS 所有裝置都提供了硬體層級的加密機制,在 Android 環境裡,資料加密是非常必須的。MMKV 使用了 AES CFB-128 演算法來加密/解密。我們選擇 CFB 而不是常見的 CBC 演算法,主要是因為 MMKV 使用 append-only 實現插入/更新操作,流式加密演算法更加合適。事實上這個功能也回饋到了 iOS 版,所以現在兩個系統的 MMKV 都有加密功能。

MMKV效能
iOS 效能對比
我們將 MMKV 和 NSUserDefaults 進行對比,重複讀寫操作 1w 次。相關測試程式碼在 iOS/MMKVDemo/MMKVDemo/,結果見如下圖表。

可見,MMKV 在寫入效能上遠遠超越 NSUserDefaults,在讀取效能上也有相近或超越的表現。

(測試機器是 iPhone X 256 G,iOS 12 beta 2,每組操作重複 1w 次,時間單位是 ms。)

Android 效能對比
我們將 MMKV 和 SharedPreferences、SQLite 進行對比, 重複讀寫操作 1k 次。相關測試程式碼在 Android/MMKV/mmkvdemo/。結果如下圖表。

• 單程式效能
可見,MMKV 在寫入效能上遠遠超越 SharedPreferences & SQLite,在讀取效能上也有相近或超越的表現。

(測試機器是 Pixel 2 XL 64G,Android 8.1,每組操作重複 1k 次,時間單位是 ms。)

• 多程式效能
可見,MMKV 無論是在寫入效能還是在讀取效能,都遠遠超越 MultiProcessSharedPreferences & SQLite & SQLite, MMKV 在 Android 多程式 key-value 儲存元件上是不二之選。

(測試機器是 Pixel 2 XL 64G,Android 8.1,每組操作重複 1k 次,時間單位是 ms。)

Github 開源地址:
https://github.com/tencent/mmkv
歡迎 Star、提 Issue 和 PR!

相關文章