MMKV--基于 mmap 的 iOS 高性能通用 key-value 组件

简介: MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。

MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。


MMKV 源起


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


MMKV 原理


内存准备


通过 mmap 内存映射文件,提供一段可供随时写入的内存块,App 只管往里面写数据,由 iOS 负责将内存回写到文件,不必担心 crash 导致数据丢失。


数据组织


数据序列化方面我们选用 protobuf 协议,pb 在性能和空间占用上都有不错的表现。考虑到我们要提供的是通用 kv 组件,key 可以限定是 string 字符串类型,value 则多种多样(int/bool/double等)。要做到通用的话,考虑将 value 通过 protobuf 协议序列化成统一的内存块(buffer),然后就可以将这些 KV 对象序列化到内存中。


微信图片_20221018114214.png

image.png


写入优化


标准 protobuf 不提供增量更新的能力,每次写入都必须全量写入。考虑到主要使用场景是频繁地进行写入更新,我们需要有增量更新的能力:将增量 kv 对象序列化后,直接 append 到内存末尾;这样同一个 key 会有新旧若干份数据,最新的数据在最后;那么只需在程序启动第一次打开 mmkv 时,不断用后读入的 value 替换之前的值,就可以保证数据是最新有效的。


空间增长


使用 append 实现增量更新带来了一个新的问题,就是不断 append 的话,文件大小会增长得不可控。例如同一个 key 不断更新的话,是可能耗尽几百 M 甚至上 G 空间,而事实上整个 kv 文件就这一个 key,不到 1k 空间就存得下。这明显是不可取的。我们需要在性能和空间上做个折中:以内存 pagesize 为单位申请空间,在空间用尽之前都是 append 模式;当 append 到文件末尾时,进行文件重整、key 排重,尝试序列化保存排重结果;排重后空间还是不够用的话,将文件扩大一倍,直到空间足够。


微信图片_20221018114222.png

image.png


数据有效性


考虑到文件系统、操作系统都有一定的不稳定性,我们另外增加了 crc 校验,对无效数据进行甄别。在 iOS 微信现网环境上,我们观察到有平均约 70w 日次的数据校验不通过。


MMKV 使用


快速上手


MMKV 提供一个全局的实例,可以直接使用:


微信图片_20221018114227.png

image.png


可以看到,MMKV 在使用上还是比较简单的。如果不同业务需要区别存储,也可以单独创建自己的实例:


微信图片_20221018114230.png

image.png


支持的数据类型


支持以下 C 语语言基础类型:


bool、int32、int64、uint32、uint64、float、double


支持以下 ObjC 类型:


NSString、NSData、NSDate


MMKV 性能


写了个简单的测试,将 MMKV、NSUserDefaults 的性能进行对比(循环写入1w 次数据,测试环境:iPhone X 256G, iOS 11.2.6,单位:ms)。


微信图片_20221018114234.png

image.png


可见 MMKV 性能远远优于 iOS 自带的 NSUserDefaults。另外,在测试中发现,NSUserDefaults 在每2-3次测试,就会有1次比较耗时的操作,怀疑是触发了数据 synchronize 重整写入。对比之下,MMKV即使触发数据重整,也保持了性能的稳定高效。



相关文章
|
6月前
|
移动开发 安全 数据安全/隐私保护
iOS 全局自动化代码混淆工具!支持 cocoapod 组件代码一并混淆
iOS 全局自动化代码混淆工具!支持 cocoapod 组件代码一并混淆
|
Android开发 Swift iOS开发
iOS14新特性探索之二:App Widget小组件应用(一)
iOS14新特性探索之二:App Widget小组件应用
497 0
iOS14新特性探索之二:App Widget小组件应用(一)
|
网络协议 开发工具 git
iOS 制作组件库上传到CocoaPods
iOS 制作组件库上传到CocoaPods
iOS 制作组件库上传到CocoaPods
|
开发框架 JavaScript 前端开发
理解iOS端的WebView同层组件
同层组件的目标是将原生组件渲染在与其他Web组件同一层级中。在iOS中,我们使用WKWebView来创建Web视图,WKWebView在进行解析渲染时,会将Web组件渲染到WKCompositingView上,这个View是一个原生的UIView子类,通常WKWebView内核会将多个组件共同渲染到同一个WKCompositingView上,但是如果某个HTML标签的style设置了overflow: scroll属性,并且内容超出容器的大小,WKWebView就会为其单独的创建一个WKChildScrollView,因此如果我们可以找到这个View,并和对应的Web组件一一关联起来,就可以将
1309 0
|
存储 缓存 安全
iOS-底层原理 35:组件化(二)组件间通讯方式
iOS-底层原理 35:组件化(二)组件间通讯方式
940 0
iOS-底层原理 35:组件化(二)组件间通讯方式
|
存储 Swift iOS开发
iOS14新特性探索之二:App Widget小组件应用(二)
iOS14新特性探索之二:App Widget小组件应用
565 0
iOS14新特性探索之二:App Widget小组件应用(二)
|
iOS开发 开发者
iOS应用内评价与购买三方APP组件
iOS应用内评价与购买三方APP组件
233 0
iOS应用内评价与购买三方APP组件
|
存储 监控 测试技术
微信团队分享:iOS版微信的高性能通用key-value组件技术实践
本文来自微信开发团队guoling的技术分享。 1、前言 本文要分享的是iOS版微信内部正在推广和使用的一个高性能通用key-value 组件的技术实践过程,该组件在微信内部被命名为MMKV(以下简称MMKV)。
2047 0
|
7天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。