MMKV—— 基于 mmap 的高性能通用 key-value 組件

MMKV 是基于 mmap 內(nèi)存映射的 key-value 組件,底層序列化 / 反序列化使用 protobuf 實(shí)現(xiàn),性能高配深,穩(wěn)定性強(qiáng)。從 2015 年中至今嫁盲,在 iOS 微信上使用已有近 3 年篓叶,其性能和穩(wěn)定性經(jīng)過了時(shí)間的驗(yàn)證。近期也已移植到 Android 平臺(tái)羞秤,一并開源缸托。

MMKV 源起

在微信客戶端的日常運(yùn)營中,時(shí)不時(shí)就會(huì)爆發(fā)特殊文字引起系統(tǒng)的 crash瘾蛋,參考文章俐镐,文章里面設(shè)計(jì)的技術(shù)方案是在關(guān)鍵代碼前后進(jìn)行計(jì)數(shù)器的加減,通過檢查計(jì)數(shù)器的異常哺哼,來發(fā)現(xiàn)引起閃退的異常文字佩抹。在會(huì)話列表、會(huì)話界面等有大量 cell 的地方取董,希望新加的計(jì)時(shí)器不會(huì)影響滑動(dòng)性能棍苹;另外這些計(jì)數(shù)器還要永久存儲(chǔ)下來 —— 因?yàn)殚W退隨時(shí)可能發(fā)生。這就需要一個(gè)性能非常高的通用 key-value 存儲(chǔ)組件茵汰,我們考察了 SharedPreferences枢里、NSUserDefaults、SQLite 等常見組件蹂午,發(fā)現(xiàn)都沒能滿足如此苛刻的性能要求栏豺。考慮到這個(gè)防 crash 方案最主要的訴求還是實(shí)時(shí)寫入豆胸,而 mmap 內(nèi)存映射文件剛好滿足這種需求奥洼,我們嘗試通過它來實(shí)現(xiàn)一套 key-value 組件。

MMKV 原理

  • 內(nèi)存準(zhǔn)備
    通過 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)增量更新帶來了一個(gè)新的問題辨图,就是不斷 append 的話,文件大小會(huì)增長(zhǎng)得不可控肢藐。我們需要在性能和空間上做個(gè)折中故河。

Android 安裝指南(android_setup_cn · Tencent/MMKV Wiki · GitHub

基本要求
  • MMKV 支持 API level 16 以上平臺(tái);
  • MMKV 需使用 NDK r16b 或以上進(jìn)行編譯 (通過源碼引入 MMKV 的話)
安裝引入

從 v1.2.8 起, MMKV 遷移到 Maven Central吆豹。老版本 (<= v1.2.7) 仍然在 JCenter鱼的。
在 App 模塊的 build.gradle 文件里添加:

dependencies {
    implementation 'com.tencent:mmkv:1.3.2'
    // replace "1.3.2" with any available version
}

Gradle 在編譯工程的時(shí)候會(huì)自動(dòng)從 maven 倉庫下載 AAR 包。

MMKV 默認(rèn)以靜態(tài)庫形式鏈接 libc++痘煤。這個(gè)庫如果動(dòng)態(tài)鏈接凑阶,會(huì)額外占用 2MB 空間(解壓后)。如果你已經(jīng)有其他庫引入了 libc++_shared.so衷快,并且你確保他們的庫沒有版本兼容問題宙橱,你可以使用動(dòng)態(tài)鏈接 libc++ 的 MMKV,以進(jìn)一步減少安裝包大姓喊巍:

dependencies {
    implementation 'com.tencent:mmkv-shared:1.3.2'
    // replace "1.3.2" with any available version
}

性能對(duì)比

循環(huán)寫入隨機(jī)的 int 1k 次师郑,我們有如下性能對(duì)比:

image.png

MMKV 支持多進(jìn)程訪問。

快速上手

實(shí)現(xiàn)應(yīng)用的存儲(chǔ)功能通常在App啟動(dòng)時(shí)就要開始初始化其對(duì)象都伪,所以我們?cè)谧远x Application 內(nèi)對(duì) MMKV 初始化呕乎。

class GlobalApplication : Application() {  
    override fun onCreate() {  
        super.onCreate()  
        // 初始化  
        String rootDir = MMKV.initialize(this)  // 返回mmkv的默認(rèn)存儲(chǔ)路徑(files/mmkv/)
    }  
}

MMKV 默認(rèn)把文件存放在$(FilesDir)/mmkv/目錄≡删В可以在 MMKV初始化時(shí)自定義根目錄

String dir = getFilesDir().getAbsolutePath() + "/mmkv"; 
String rootDir = MMKV.initialize(dir);

MMKV 創(chuàng)建實(shí)例:

// 獲取 MMKV 默認(rèn)全局實(shí)例,一般選用這個(gè)帝璧,本文也是選擇這個(gè)進(jìn)行實(shí)例創(chuàng)建
val mmkv = MMKV.defaultMMKV()

// 根據(jù)設(shè)置 id 來自定義 MMKV 對(duì)象先誉。比如根據(jù)業(yè)務(wù)來區(qū)分的存取實(shí)例
val mmkv = MMKV.mmkvWithID("ID")  

// 開啟多進(jìn)程訪問。默認(rèn)是單線程
val mmkv = MMKV.mmkvWithID("ID",MMKV.MULTI_PROCESS_MODE)

寫數(shù)據(jù):

kv.encode("bool", true);
kv.encode("int", Integer.MIN_VALUE);
kv.encode("string", "Hello from mmkv");

讀數(shù)據(jù):

boolean bValue = kv.decodeBool("bool");
int iValue = kv.decodeInt("int");
String str = kv.decodeString("string");

刪除數(shù)據(jù):

刪除單個(gè)key:removeValueForKey(String key)
刪除多個(gè)key:removeValuesForKeys(String[] arrKeys)
清空所有數(shù)據(jù):clearAll()

從 SharedPreferences 遷移到 MMKV

為了演示怎么遷移的烁,我們重頭再來褐耳,在準(zhǔn)備部分的代碼基礎(chǔ)上臨時(shí)創(chuàng)建 SharedPreferences 儲(chǔ)存數(shù)據(jù)。

val sp = getSharedPreferences("test", Context.MODE_PRIVATE)  
val edit = sp.edit()  
// 為了驗(yàn)證順利遷移渴庆,我們初始值設(shè)置為10  
edit.putInt("number",10);  
edit.apply()

MMKV 提供了 importFromSharedPreferences 方法讓我們方便進(jìn)行 SP 數(shù)據(jù)的遷移铃芦,只要提供需要遷移的 SP 實(shí)例就能非逞拍鳎快速完成實(shí)例。
MMKV 實(shí)現(xiàn)了 SharedPreferences刃滓,Editor兩個(gè)接口仁烹,所以在遷移之后SP的操作代碼可以不用更改。

val oldData = getSharedPreferences("test",Context.MODE_PRIVATE)  
mmkv.importFromSharedPreferences(oldData)  
oldData.edit().clear().apply()

參考地址:MMKV首頁咧虎、文檔和下載 - 基于 mmap 的高性能通用 key-value 組件 - OSCHINA - 中文開源技術(shù)交流社區(qū)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末卓缰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子砰诵,更是在濱河造成了極大的恐慌征唬,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茁彭,死亡現(xiàn)場(chǎng)離奇詭異总寒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)理肺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門偿乖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人哲嘲,你說我怎么就攤上這事贪薪。” “怎么了眠副?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵画切,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我囱怕,道長(zhǎng)霍弹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任娃弓,我火速辦了婚禮典格,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘台丛。我一直安慰自己耍缴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布挽霉。 她就那樣靜靜地躺著防嗡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪侠坎。 梳的紋絲不亂的頭發(fā)上蚁趁,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音实胸,去河邊找鬼他嫡。 笑死番官,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钢属。 我是一名探鬼主播徘熔,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼署咽!你這毒婦竟也來了近顷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤宁否,失蹤者是張志新(化名)和其女友劉穎窒升,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體慕匠,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饱须,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了台谊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蓉媳。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锅铅,靈堂內(nèi)的尸體忽然破棺而出酪呻,到底是詐尸還是另有隱情,我是刑警寧澤盐须,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布玩荠,位于F島的核電站,受9級(jí)特大地震影響贼邓,放射性物質(zhì)發(fā)生泄漏阶冈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一塑径、第九天 我趴在偏房一處隱蔽的房頂上張望女坑。 院中可真熱鬧,春花似錦统舀、人聲如沸匆骗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绰筛。三九已至,卻和暖如春描融,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背衡蚂。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國打工窿克, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留骏庸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓年叮,卻偏偏與公主長(zhǎng)得像具被,于是被迫代替她去往敵國和親只损。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容