MMKV( 一) 了解原理

前言:由于知識點多,分了多個記錄。

MMKV( 一) 了解原理
MMKV (二)基礎(chǔ)知識點和實現(xiàn)流程解析
MMKV (三) POSIX線程和文件鎖

導(dǎo)言:MMKV 可以多進(jìn)程通信,實際上就是共享文件的方式,是基于 mmap 內(nèi)存映射的 key-value 組件,底層序列化/反序列化使用 protobuf 實現(xiàn),性能高,穩(wěn)定性強(qiáng),可以跨進(jìn)程

和SharePreference 對比

sharepreference獲取.png

SharePreference 源碼實現(xiàn) SharePreferenceImpl.java 對xml的讀寫


sharepreference讀取.png

不能跨進(jìn)程,如果非要跨進(jìn)程配合ContentProvider使用。

讀寫方式:I/O
數(shù)據(jù)格式:XML
k-v 更新:全量更新,n個k-v->序列化成xml io->文件

MMKV 對比

I/O vs mmap
數(shù)據(jù)格式:
1、總體結(jié)構(gòu)
2、整型編碼
3、計算長度
增量與全量寫入

mmap 和 mmap的優(yōu)缺點

Linux通過將一個虛擬內(nèi)存區(qū)域與一個磁盤上的對象關(guān)聯(lián)起來,以初始化這個虛擬內(nèi)存區(qū)域的內(nèi)容,這個過程稱為內(nèi)存映射(memory mapping)。

mmap模型.png

對文件進(jìn)行mmap,會在進(jìn)程的虛擬內(nèi)存分配地址空間,創(chuàng)建映射關(guān)系

實現(xiàn)這樣的映射關(guān)系后,就可以采用指針的方式讀寫操作這一段內(nèi)存,而系統(tǒng)會自動回寫到對應(yīng)的文件磁盤上

I/O 在系統(tǒng)的操作流程

虛擬內(nèi)存被操作系統(tǒng)劃分成兩塊:用戶空間和內(nèi)核空間,用戶空間是用戶程序代碼運行的地方,內(nèi)核空間是內(nèi)核代碼運行的地方。為了安全,它們是隔離的,即使用戶的程序崩潰了,內(nèi)核也不受影響。

IO讀寫模型.png

寫文件流程:

1、調(diào)用write,告訴內(nèi)核需要寫入數(shù)據(jù)的開始地址與長度
2、內(nèi)核將數(shù)據(jù)拷貝到內(nèi)核緩存
3、由操作系統(tǒng)調(diào)用,將數(shù)據(jù)拷貝到磁盤,完成寫入

優(yōu)點
  • MMAP對文件的讀寫操作只需要從磁盤到用戶主存的一次數(shù)據(jù)拷貝過程,減少了數(shù)據(jù)的拷貝次數(shù),提高了文件讀寫效率。
  • MMAP使用邏輯內(nèi)存對磁盤文件進(jìn)行映射,操作內(nèi)存就相當(dāng)于操作文件,不需要開啟線程,操作MMAP的速度和操作內(nèi)存的速度一樣快;
  • MMAP提供一段可供隨時寫入的內(nèi)存塊,App 只管往里面寫數(shù)據(jù),由操作系統(tǒng)如內(nèi)存不足、進(jìn)程退出等時候負(fù)責(zé)將內(nèi)存回寫到文件,不必?fù)?dān)心 crash 導(dǎo)致數(shù)據(jù)丟失。

參考微信和美團(tuán)
微信 Mars : https://mp.weixin.qq.com/s/kDPTt9Rtd-PERXXW-UyUlQ

美團(tuán) Logan : https://tech.meituan.com/2018/02/11/logan.html

防止數(shù)據(jù)丟失并提升了讀寫效率。

寫入內(nèi)存 寫入mmap 寫入文件
OS X 17ms 18ms 2685ms
iOS 55ms 55ms 13116ms
Android 283ms 2488ms 7583ms

三大優(yōu)勢

mmap防止數(shù)據(jù)丟失,提高讀寫效率;

精簡數(shù)據(jù),以最少的數(shù)據(jù)量表示最多的信息,減少數(shù)據(jù)大小;

增量更新,避免每次進(jìn)行相對增量來說大數(shù)據(jù)量的全量寫入

缺點

因為我們是按照頁存儲方式,每頁4096字節(jié),我們只有100字節(jié)使用,那么整個頁4096字節(jié)映射,所以造成一定的浪費

MMKV 存儲的數(shù)據(jù)結(jié)構(gòu)

mmkv數(shù)據(jù)結(jié)構(gòu).png

對比xml 結(jié)構(gòu)

xml樣例.png

整型編碼

1個字節(jié)保存7位數(shù)據(jù)
如果寫入的數(shù)據(jù) <= 0x7f 那么使用7位即1個字節(jié)表示。
如果寫入的數(shù)據(jù) > 0x7f 那么先記錄低7位數(shù)據(jù),并將最高位設(shè)為1,繼續(xù)執(zhí)行判斷

整型編碼.png

寫入方式

增量寫入:
不管key是否重復(fù),直接將數(shù)據(jù)追加在前數(shù)據(jù)后。

如果不斷 增量 追加內(nèi)容,文件越來越大,怎么辦?

全量寫入:
當(dāng)文件大小不夠,這時候需要全量寫入。將數(shù)據(jù)去掉重復(fù)key后,如果文件大小滿足寫入的數(shù)據(jù)大小,則可以直接更新全量寫入,否則需要擴(kuò)容。(在擴(kuò)容時根據(jù)平均每個K-V大小計算未來可能需要的文件大小進(jìn)行擴(kuò)容,防止經(jīng)常性的全量寫入)

擴(kuò)容

擴(kuò)容非常簡單,只需要重新設(shè)定文件大小,然后重新mmap映射即可

//重新設(shè)定文件大小
ftruncate(m_fd, m_size);
//解除映射
munmap(m_ptr, oldSize);
//重新映射
m_ptr = (int8_t *) mmap(m_ptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);

存儲的小例子

010 編輯器查看截圖,總長度-》key長度-》value長度

沒有xml 的冗余數(shù)據(jù),更小,更方便增量更新。總長度改下,k-v 在后面追加就可以了。內(nèi)存中的map集合中永遠(yuǎn)是最新的數(shù)據(jù)。

Protobuf 變長編碼 例子

編碼65280-》

1111 1111 0000 0000

取低七位:000 0000,最高位補(bǔ)1(表示還需要下一個字節(jié))

1000 0000 -》寫出

右移7位:1111 1111 0

取低七位:111 1110,最高位補(bǔ)1

1111 1110 -》 寫出
右移7位:11

小于0x7f,最高位不需要設(shè)置為1

0000 0011 -》寫出

解析:

1000 0000
1111 1110
0000 0011

拿后7位,7個0
000 0000高位是1需要繼續(xù)獲取
再次拿后7位
111 1110 高位是1需要繼續(xù)獲取拼接 111 1110 000 0000
再次拿后7位
000 0011 高位0,不許繼續(xù),直接拼接去掉頭部無用0
000 0011111 1110 000 0000

最后整理之后
1111 1111 0000 0000

被編譯成了3個字節(jié),定長編碼應(yīng)該是4個字節(jié),節(jié)省1個字節(jié)


1頁4096 字節(jié),linux 按照頁方式存儲,超過1頁數(shù)據(jù)量發(fā)生頁中斷。

參考MMKV 文檔

官方項目MMKV project

下一篇文章 MMKV(二) 基礎(chǔ)知識和核心流程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

友情鏈接更多精彩內(nèi)容