本文轉(zhuǎn)自微信開發(fā)團(tuán)隊(duì)凌國(guó)的分享墙懂。原文
MMKV 是基于 mmap 內(nèi)存映射的移動(dòng)端通用 key-value 組件弃榨,底層序列化/反序列化使用 protobuf 實(shí)現(xiàn)挚赊,性能高抓艳,穩(wěn)定性強(qiáng)。從 2015 年中至今遭笋,在 iOS 微信上使用已有近 3 年坝冕,其性能和穩(wěn)定性經(jīng)過(guò)了時(shí)間的驗(yàn)證徒探。近期已移植到 Android 平臺(tái)瓦呼。在騰訊內(nèi)部開源半年之后,得到公司內(nèi)部團(tuán)隊(duì)的廣泛應(yīng)用和一致好評(píng)。現(xiàn)在一并對(duì)外開源:
https://github.com/tencent/mmkv
歡迎 Star央串、提 Issue 和 PR磨澡。
MMKV 源起
在微信客戶端的日常運(yùn)營(yíng)中,時(shí)不時(shí)就會(huì)爆發(fā)特殊文字引起系統(tǒng)的 crash质和,參考文章稳摄,文章里面設(shè)計(jì)的技術(shù)方案是在關(guān)鍵代碼前后進(jìn)行計(jì)數(shù)器的加減,通過(guò)檢查計(jì)數(shù)器的異常饲宿,來(lái)發(fā)現(xiàn)引起閃退的異常文字厦酬。在會(huì)話列表、會(huì)話界面等有大量 cell 的地方瘫想,希望新加的計(jì)時(shí)器不會(huì)影響滑動(dòng)性能仗阅;另外這些計(jì)數(shù)器還要永久存儲(chǔ)下來(lái)——因?yàn)殚W退隨時(shí)可能發(fā)生。這就需要一個(gè)性能非常高的通用 key-value 存儲(chǔ)組件国夜,我們考察了 SharedPreferences减噪、NSUserDefaults、SQLite 等常見(jiàn)組件车吹,發(fā)現(xiàn)都沒(méi)能滿足如此苛刻的性能要求筹裕。考慮到這個(gè)防 crash 方案最主要的訴求還是實(shí)時(shí)寫入窄驹,而 mmap 內(nèi)存映射文件剛好滿足這種需求朝卒,我們嘗試通過(guò)它來(lái)實(shí)現(xiàn)一套 key-value 組件。
MMKV 原理
內(nèi)存準(zhǔn)備
通過(guò) mmap 內(nèi)存映射文件乐埠,提供一段可供隨時(shí)寫入的內(nèi)存塊扎运,App 只管往里面寫數(shù)據(jù),由操作系統(tǒng)負(fù)責(zé)將內(nèi)存回寫到文件饮戳,不必?fù)?dān)心 crash 導(dǎo)致數(shù)據(jù)丟失豪治。數(shù)據(jù)組織
數(shù)據(jù)序列化方面我們選用 protobuf 協(xié)議,pb 在性能和空間占用上都有不錯(cuò)的表現(xiàn)扯罐。寫入優(yōu)化
考慮到主要使用場(chǎng)景是頻繁地進(jìn)行寫入更新负拟,我們需要有增量更新的能力。我們考慮將增量 kv 對(duì)象序列化后歹河,append 到內(nèi)存末尾掩浙。空間增長(zhǎng)
使用 append 實(shí)現(xiàn)增量更新帶來(lái)了一個(gè)新的問(wèn)題,就是不斷 append 的話秸歧,文件大小會(huì)增長(zhǎng)得不可控厨姚。我們需要在性能和空間上做個(gè)折中。
更詳細(xì)的設(shè)計(jì)原理參考前文 《MMKV——iOS 下基于 mmap 的高性能通用 key-value 組件》键菱。
MMKV for Android 特有功能
我們不是簡(jiǎn)簡(jiǎn)單單地照搬 iOS 的實(shí)現(xiàn)谬墙,在遷移到 Android 的過(guò)程中,深入分析了 Android 平臺(tái)現(xiàn)有 kv 組件的痛點(diǎn),在原有功能基礎(chǔ)上拭抬,開發(fā)了 Android 特有的功能部默。
多進(jìn)程訪問(wèn)
通過(guò)與 Android 開發(fā)同學(xué)的溝通,了解到系統(tǒng)自帶的 SharedPreferences 對(duì)多進(jìn)程的支持不好≡旎ⅲ現(xiàn)有基于 ContentProvider 封裝的實(shí)現(xiàn)傅蹂,雖然多進(jìn)程是支持了,但是性能低下算凿,經(jīng)常導(dǎo)致 ANR份蝴。考慮到 mmap 共享內(nèi)存本質(zhì)上的多進(jìn)程共享的氓轰,我們?cè)谶@個(gè)基礎(chǔ)上搞乏,深入挖掘了 Android 系統(tǒng)的能力,提供了可能是業(yè)界最高效的多進(jìn)程數(shù)據(jù)共享組件戒努。具體實(shí)現(xiàn)原理我們中秋節(jié)后分享请敦,心急的同學(xué)可以前往 GitHub 查看源碼和 wiki 文檔。匿名內(nèi)存
在多進(jìn)程共享的基礎(chǔ)上储玫,考慮到某些敏感數(shù)據(jù)(例如密碼)需要進(jìn)程間共享侍筛,但是不方便落地存儲(chǔ)到文件上,直接用 mmap 不合適撒穷。我們了解到 Android 系統(tǒng)提供了 Ashmem 匿名共享內(nèi)存的能力匣椰,發(fā)現(xiàn)它在進(jìn)程退出后就會(huì)消失,不會(huì)落地到文件上端礼,非常適合這個(gè)場(chǎng)景禽笑。我們很愉快地提供了 Ashmem MMKV 的功能。數(shù)據(jù)加密
不像 iOS 提供了硬件層級(jí)的加密機(jī)制蛤奥,在 Android 環(huán)境里佳镜,數(shù)據(jù)加密是非常必須的。MMKV 使用了 AES CFB-128 算法來(lái)加密/解密凡桥。我們選擇 CFB 而不是常見(jiàn)的 CBC 算法蟀伸,主要是因?yàn)?MMKV 使用 append-only 實(shí)現(xiàn)插入/更新操作,流式加密算法更加合適缅刽。事實(shí)上這個(gè)功能也回饋到了 iOS 版啊掏,所以現(xiàn)在兩個(gè)系統(tǒng)的 MMKV 都有加密功能。
MMKV 使用
iOS 的使用在前文已經(jīng)陳述衰猛,這里簡(jiǎn)單介紹一下 Android 的用法迟蜜。
Android 快速上手
MMKV 已托管到 bintray(JCenter),可以直接使用啡省。在 App 的 build.gradle 里加上依賴:
MMKV 的使用非常簡(jiǎn)單娜睛,所有變更立馬生效髓霞,無(wú)需調(diào)用 sync
、apply
微姊。 在 App 啟動(dòng)時(shí)初始化 MMKV酸茴,設(shè)定 MMKV 的根目錄(files/mmkv/)分预,例如在 MainActivity
里:
MMKV 提供一個(gè)全局的實(shí)例兢交,可以直接使用:
如果不同業(yè)務(wù)需要區(qū)別存儲(chǔ),也可以單獨(dú)創(chuàng)建自己的實(shí)例:
SharedPreferences 遷移
MMKV 提供了 importFromSharedPreferences() 函數(shù)笼痹,可以比較方便地遷移數(shù)據(jù)過(guò)來(lái)配喳。
MMKV 還額外實(shí)現(xiàn)了一遍 SharedPreferences、SharedPreferences.Editor 這兩個(gè) interface凳干,在遷移的時(shí)候只需兩三行代碼即可晴裹,其他 CRUD 操作代碼都不用改。
更詳細(xì)的用法可以參看 GitHub 上的 wiki 文檔救赐。
MMKV 性能
iOS 性能對(duì)比
我們將 MMKV 和 NSUserDefaults 進(jìn)行對(duì)比涧团,重復(fù)讀寫操作 1w 次。相關(guān)測(cè)試代碼在 iOS/MMKVDemo/MMKVDemo/
经磅,結(jié)果見(jiàn)如下圖表泌绣。
(測(cè)試機(jī)器是 iPhone X 256 G,iOS 12 beta 2预厌,每組操作重復(fù) 1w 次阿迈,時(shí)間單位是 ms。)
可見(jiàn)轧叽,MMKV 在寫入性能上遠(yuǎn)遠(yuǎn)超越 NSUserDefaults苗沧,在讀取性能上也有相近或超越的表現(xiàn)。
Android 性能對(duì)比
我們將 MMKV 和 SharedPreferences炭晒、SQLite 進(jìn)行對(duì)比, 重復(fù)讀寫操作 1k 次待逞。相關(guān)測(cè)試代碼在 Android/MMKV/mmkvdemo/
。結(jié)果如下圖表网严。
-
單進(jìn)程性能
可見(jiàn)飒焦,MMKV 在寫入性能上遠(yuǎn)遠(yuǎn)超越 SharedPreferences & SQLite,在讀取性能上也有相近或超越的表現(xiàn)屿笼。(測(cè)試機(jī)器是 Pixel 2 XL 64G牺荠,Android 8.1,每組操作重復(fù) 1k 次驴一,時(shí)間單位是 ms休雌。)
-
多進(jìn)程性能
可見(jiàn),MMKV 無(wú)論是在寫入性能還是在讀取性能肝断,都遠(yuǎn)遠(yuǎn)超越 MultiProcessSharedPreferences & SQLite & SQLite杈曲, MMKV 在 Android 多進(jìn)程 key-value 存儲(chǔ)組件上是不二之選驰凛。(測(cè)試機(jī)器是 Pixel 2 XL 64G,Android 8.1担扑,每組操作重復(fù) 1k 次恰响,時(shí)間單位是 ms。)
點(diǎn)擊原文直接訪問(wèn) GitHub 源碼涌献。