MMKV概览

1 MMKV概览

1.1 什么是MMKV

引自 github.com/Tencent/MMKV 介绍[https://github.com/Tencent/MMKV/blob/master/readme_cn.md]

MMKV——基于 mmap 的高性能通用 key-value 组件
MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能 高,稳定性强。从 2015 年中至今在微信上使用,其性能和稳定性经过了时间的验证。近期也已移 植到 Android / macOS / Windows 平台,一并开源。

MMKV 源起
在微信客户端的日常运营中,时不时就会爆发特殊文字引起系统的 crash,文章里面设计的技术方 案是在关键代码前后进行计数器的加减,通过检查计数器的异常,来发现引起闪退的异常文字。 在会话列表、会话界面等有大量 cell 的地方,希望新加的计时器不会影响滑动性能;另外这些计 数器还要永久存储下来——因为闪退随时可能发生。这就需要一个性能非常高的通用 key-value 存 储组件,我们考察了 SharedPreferences、NSUserDefaults、SQLite 等常见组件,发现都没能满足如 此苛刻的性能要求。考虑到这个防 crash 方案最主要的诉求还是实时写入,而 mmap 内存映射文 件刚好满足这种需求,我们尝试通过它来实现一套 key-value 组件。
MMKV 原理

  • 内存准备
    通过 mmap 内存映射文件,提供一段可供随时写入的内存块,App 只管往里面写数据,由操 作系统负责将内存回写到文件,不必担心 crash 导致数据丢失。
  • 数据组织
    数据序列化方面我们选用 protobuf 协议,pb 在性能和空间占用上都有不错的表现。
  • 写入优化
    考虑到主要使用场景是频繁地进行写入更新,我们需要有增量更新的能力。我们考虑将增量 kv 对象序列化后,append 到内存末尾。
  • 空间增长
    使用 append 实现增量更新带来了一个新的问题,就是不断 append 的话,文件大小会增长得不可 控。我们需要在性能和空间上做个折中。

1.2 SharedPreference简介

SharedPreference 是安卓系统数据持久化的一种选择,用于存储轻量级 K-V 数据。SP 的读写方式为直接 IO 读取,即通过 FileInputStream/FileOutputStream 来进行读写,数据格式为 XML ,写入方式为全量更新。

SharedPreference

1.3 直接I/O读取与mmap

1.3.1 直接I/O

虚拟内存被操作系统划分成两块:用户空间和内核空间,用户空间是用户程序代码运行的地方,内核空 间是内核代码运行的地方。为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。

直接I/O写文件流程
  1. 调用 write ,告诉内核需要写入数据的开始地址与长度
  2. 内核将数据拷贝到内核缓存
  3. 由操作系统调用,将数据拷贝到磁盘,完成写入

1.3.2 mmap

Linux 通过将一个虚拟内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射 (memory mapping)
对文件进行 mmap,会在进程的虚拟内存分配地址空间,创建映射关系。实现这样的映射关系后,就可 以采用指针的方式读写操作这一段内存,而系统会自动回写到对应的文件。

mmap

1.3.3 mmap的优势

  • mmap 对文件的读写操作只需要从磁盘到用户主存的一次数据拷贝过程,减少了数据的拷贝次数, 提高了文件读写效率
  • mmap 使用逻辑内存对磁盘文件进行映射,操作内存就相当于操作文件,不需要开启线程,操作 mmap 的速度和操作内存的速度一样快
  • mmap 提供一段可供随时写入的内存块,App 只管往里面写数据,由操作系统如内存不足、进程退出等时候负责将内存回写到文件,不必担心 crash 导致数据丢失(见文首引文)
image-20201222150725142

1.4 MMKV数据结构

image-20201222150912350
image-20201222150926484

使用 010editor 打开 MMKV 映射文件

  • 前4个字节表示整个映射文件中数据的有效长度

    Q:为什么需要有效长度?

    A:因为 mmap 内存映射的时候,文件大小必须是 4096 (这个数字与操作系统位数有关)或者 其整数倍,所以并非整个文件内容都是有效数据,需要用有效数据长度来标明有效数据。

  • 从第五个字节开始,依次为k1长--->k1值--->v1长--->v1值--->k2长--->k2值--->v2长--->v2值---> ... --->

1.5 MMKV增量修改

我们介绍了 MMKV 数据在文件中的存储结构,这种结构如何实现增量修改的呢?

原因是 MMKV 在内存中是一个 map 表结构。完成首次 mmap 映射关系的建立后,之后我再次写入一个 同名 key 的 键值对,该键值对直接存储在映射文件的末尾,并修改有效长度。当我映射关系断开后重新 建立映射关系的时候,旧的键值对先写入 map 表,新的键值对后读入,将会覆盖掉旧的键值对,也就 实现了增量修改。

1.6 protobuf编码

1.6.1 protobuf编码规则

protobuf编码规则

我们以整型数据来说明
每一个字节的首位作标志位, 标志位为1 ,则表示该字节无法完整表示数据,需要更多的字节; 标志位 为0 ,则表示该字节已经是表示该数据的最后一个字节,该数据的读取到该字节为止。每一个字节的后 七位保存数据。

0x7f ?
ox7f 的10进制表示为 127 ,2 进制表示为 0111 1111 。
如果写入的数据 <= 0x7f ,那么一个字节的七个数据位足够表示这个数据,则字节首位置 0 ,后七位写入数据。
如果写入的数据 > 0x7f 那么一个字节的七个数据位不足以表示这个数据,则字节首位置1,后七位 写入数据,并将原数右移 7 位,继续执行判断。

1.6.2 protobuf编码举例

  • 编码 128 ,即 1000 0000

    1. 128 <= 127 ? (或者“1000 0000 用 7 位能否完整表示?”) NO!则字节首位置 1 ,后七位写入数 据 000 0000 ,得到第一个字节 1000 0000 ,将 128 右移 7 位,得到 0000 0001;

    2. 1 <= 127 ?(或者“0000 0001 用 7 位能否完整表示?”)YES!则字节首位置 0 ,后七位写入数据 000 0001 ,得到编码的最后一个字节 0000 0001 。

    3. 128 最后的 protobuf 编码为 1000 0000 | 0000 0001 ,从原本 int 的 4 字节,压缩到了现在的 2 字节

  • 读取 128 的 protobuf 编码,即 1000 0000 | 0000 0001

    1. 从前往后读取,读到第一个字节,即 1000 0000 ,首位为 1 ,说明该字节非表示该数据的最 后一个字节,则取出数据位 000 0000 暂存;

    2. 从前往后读取,读到第二个字节,即 0000 0001 ,首位为 0 ,说明该字节是表示该数据的最 后一个字节,则取出数据位 000 0001 暂存;

    3. 要明确,先存入的是低位,即先读出的也是低位。所以 后读出的字节要拼接到先读出字节的 前面构成高位,所以读取 1000 0000 0000 0001 结果是 000 0001 000 0000 ,即 1000 0000 或 128 。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容