1. mmap高性能存取的基石
MMKV通過mmap 內存映射文件來進行讀寫操作的,這是其效率高于普通IO的原因。
普通IO
傳統(tǒng)的read首先將文件內容從硬盤拷貝到內核空間的一個緩沖區(qū),然后再將這些數據拷貝到用戶空間,這個過程中,實際上完成了。
內存映射
mmap將文件直接映射到用戶空間,所以中斷處理函數根據這個映射關系,直接將文件從硬盤拷貝到用戶空間,只進行了 ,因此,mmap內存映射的效率要比read/write效率高。
為什么不直接用內存映射代替IO?
既然MMKV使用的內存映射優(yōu)于IO,為什么還要使用IO?
首先要明白,直接將文件映射到虛擬內存,意味著沒有數據沒有緩存在內核緩存空間,而是直接讀到了用戶空間,而系統(tǒng)的IO和內核緩存搭配可以使得部分的文件使用效率更高。(OS會根據局部性原理在一次read()系統(tǒng)調用的時候預讀取更多的文件數據到內核空間緩沖區(qū)中,這樣當下一次read()系統(tǒng)調用的時候發(fā)現要讀取的數據已經存在于內核空間緩沖區(qū)中的時候只要直接拷貝數據到用戶空間緩沖區(qū)中即可,無需再進行一次低效的磁盤I/O操作,且磁盤的大小要遠遠超過內存)
而且mmap映射的文件是大于一個內存頁大小的(),并且是
。
也就是說兩個方式都是有優(yōu)缺點的,所以不存在代替這個說法,只能通過分析其場景而選擇不同的方式。
2.采用Protobuf協議存儲key-value結構
Protobuf協議
protobuf 是google開源的一個序列化框架,類似xml,json,最大的特點是基于二進制,比SharedPreferences使用的傳統(tǒng)的XML表示同樣一段內容要短小得多。同樣這也不能說明Protobuf優(yōu)于XML,關于Protobuf的更多內容如下:
寫入優(yōu)化
標準 protobuf 和SharedPreferences 一樣,每次寫入kv對象都必須全量寫入。也就是寫入之前將所有數據加載到內存中,然后判斷新增的key是否已經存在,完成更新或增加后在全部寫入文件。
MMKV中采用增量更新的方式處理protobuf,當需要寫入kv對象時,不論是新增還是更新都將其直接加入文件的末尾,這樣大大增加了寫入效率。
上面的做法必然帶來兩個問題
1.必然導致同一key值會有新舊若干份數據,最新的數據在最后。
2.文件大小會增長得不可控。同一個 key 不斷更新的話,是可能耗盡幾百 M 甚至上 G 空間。
針對第一個問題,在讀取時,針對同一個 key使用后讀入的 value 替換之前的值,就可以保證數據是最新有效的。
針對第二個問題,有上文可知MMKV的文件必然是稍大于(一個內存頁的大小)的倍數,當寫入的數據小于4k時,可以繼續(xù)寫入,因為本身文件大小就已經略大于4k了,有點很小的浪費,當寫入數據超過4k的倍數后,進行文件重整、key 排重,嘗試序列化保存排重結果;排重后空間還是不夠用的話,將文件在增加4k,直到空間足夠。
3.通過crc 校驗確保數據有效性
文件系統(tǒng)、操作系統(tǒng)都有一定的不穩(wěn)定性,MMKV使用crc 校驗確保數據有效性,關于crc 校驗,可以參考:
4.多進程設計與實現
這一步官方有詳盡的說明,如下:
多進程設計與實現