微信团队分享:iOS版微信的高性能通用key-value组件技术实践
本文来自微信开发团队guoling的技术分享。
1、前言
本文要分享的是iOS版微信内部正在推广和使用的一个高性能通用key-value 组件的技术实践过程,该组件在微信内部被命名为MMKV(以下简称MMKV)。
MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。希望对于有高性能key-value 组件或类似技术需求的IM同行,能通过本文获得一定的启发。
学习交流:
- 即时通讯开发交流群:320837163[推荐]
- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM》
(本文同步发布于:http://www.52im.net/thread-1461-1-1.html)
2、MMKV 源起
在 iOS 微信的日常运营中,时不时就会爆发特殊文字引起 iOS 系统的 crash,《微信团队分享:iOS版微信是如何防止特殊字符导致的炸群、APP崩溃的?》一文里面设计的技术方案是在关键代码前后进行计数器的加减,通过检查计数器的异常,来发现引起闪退的异常文字。
《微信团队分享:iOS版微信是如何防止特殊字符导致的炸群、APP崩溃的?》里设计的技术方案大致原理就是:
1)在会话列表、会话界面等有大量 cell 的地方,希望新加的计时器不会影响滑动性能;
2)这些计数器还要永久存储下来——因为闪退随时可能发生。
这就需要一个性能非常高的通用 key-value 存储组件,我们考察了 NSUserDefaults、SQLite 等常见组件,发现都没能满足如此苛刻的性能要求。考虑到这个防 crash 方案最主要的诉求还是实时写入,而 mmap 内存映射文件刚好满足这种需求,我们尝试通过它来实现一套 key-value 组件。
3、MMKV 原理
3.1 内存准备
通过 mmap 内存映射文件,提供一段可供随时写入的内存块,App 只管往里面写数据,由 iOS 负责将内存回写到文件,不必担心 crash 导致数据丢失。
3.2 数据组织
数据序列化方面我们选用 protobuf 协议,pb 在性能和空间占用上都有不错的表现。考虑到我们要提供的是通用 kv 组件,key 可以限定是 string 字符串类型,value 则多种多样(int/bool/double等)。要做到通用的话,考虑将 value 通过 protobuf 协议序列化成统一的内存块(buffer),然后就可以将这些 KV 对象序列化到内存中。

更多有关Protobuf的文章请见:
《强列建议将Protobuf作为你的即时通讯应用数据传输格式》
《全方位评测:Protobuf性能到底有没有比JSON快5倍?》
《一个基于Protocol Buffer的Java代码演示》
《详解如何在NodeJS中使用Google的Protobuf》
3.3 写入优化
标准 protobuf 不提供增量更新的能力,每次写入都必须全量写入。
考虑到主要使用场景是频繁地进行写入更新,我们需要有增量更新的能力:
1)将增量 kv 对象序列化后,直接 append 到内存末尾;
2)这样同一个 key 会有新旧若干份数据,最新的数据在最后;
3)那么只需在程序启动第一次打开 mmkv 时,不断用后读入的 value 替换之前的值,就可以保证数据是最新有效的。
3.4 空间增长
使用 append 实现增量更新带来了一个新的问题,就是不断 append 的话,文件大小会增长得不可控。例如同一个 key 不断更新的话,是可能耗尽几百 M 甚至上 G 空间,而事实上整个 kv 文件就这一个 key,不到 1k 空间就存得下。这明显是不可取的。
我们需要在性能和空间上做个折中:
1)以内存 pagesize 为单位申请空间,在空间用尽之前都是 append 模式;
2)当 append 到文件末尾时,进行文件重整、key 排重,尝试序列化保存排重结果;
3)排重后空间还是不够用的话,将文件扩大一倍,直到空间足够。

3.5 数据有效性
考虑到文件系统、操作系统都有一定的不稳定性,我们另外增加了 crc 校验,对无效数据进行甄别。在 iOS 微信现网环境上,我们观察到有平均约 70w 日次的数据校验不通过。
4、MMKV 使用
4.1 快速上手
MMKV 提供一个全局的实例,可以直接使用:

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

4.2 支持的数据类型
支持以下 C 语语言基础类型:
bool、int32、int64、uint32、uint64、float、double
支持以下 ObjC 类型:
NSString、NSData、NSDate
5、MMKV 性能
写了个简单的测试,将 MMKV、NSUserDefaults 的性能进行对比(循环写入1w 次数据,测试环境:iPhone X 256G, iOS 11.2.6,单位:ms)。

可见 MMKV 性能远远优于 iOS 自带的 NSUserDefaults。另外,在测试中发现,NSUserDefaults 在每2-3次测试,就会有1次比较耗时的操作,怀疑是触发了数据 synchronize 重整写入。对比之下,MMKV即使触发数据重整,也保持了性能的稳定高效。
目前 MMKV 已经在鹅厂内部开源(http://git.code.oa.com/wechat-team/mmkv),反馈比较好的话会考虑对外开源。
(原文链接:https://mp.weixin.qq.com/s/cZQ3FQxRJBx4px1woBaasg,本文略有改动)
附录:有关微信、QQ的文章汇总
[1] QQ、微信团队原创技术文章:
《微信团队分享:iOS版微信的高性能通用key-value组件技术实践》
《微信团队分享:iOS版微信是如何防止特殊字符导致的炸群、APP崩溃的?》
《腾讯技术分享:Android手Q的线程死锁监控系统技术实践》
《QQ音乐团队分享:Android中的图片压缩技术详解(上篇)》
《QQ音乐团队分享:Android中的图片压缩技术详解(下篇)》
《腾讯团队分享 :一次手Q聊天界面中图片显示bug的追踪过程分享》
《微信团队分享:微信Android版小视频编码填过的那些坑》
《微信团队披露:微信界面卡死超级bug“15。。。。”的来龙去脉》
《月活8.89亿的超级IM微信是如何进行Android端兼容测试的》
《微信客户端团队负责人技术访谈:如何着手客户端性能监控和优化》
《微信团队原创分享:Android版微信的臃肿之困与模块化实践之路》
《微信团队原创分享:微信客户端SQLite数据库损坏修复实践》
《腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率》
《腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(下篇)》
《腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(上篇)》
《如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源》
《开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石 [源码下载]》
《微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解》
《微信团队原创分享:Android版微信后台保活实战分享(进程保活篇)》
《微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)》
《Android版微信从300KB到30MB的技术演进(PPT讲稿) [附件下载]》
《微信团队原创分享:Android版微信从300KB到30MB的技术演进》
《微信技术总监谈架构:微信之道——大道至简(PPT讲稿) [附件下载]》
《微信海量用户背后的后台系统存储架构(视频+PPT) [附件下载]》
《微信异步化改造实践:8亿月活、单机千万连接背后的后台解决方案》
《架构之道:3个程序员成就微信朋友圈日均10亿发布量[有视频]》
《微信团队原创分享:Android内存泄漏监控和优化技巧总结》
《微信团队原创Android资源混淆工具:AndResGuard [有源码]》
《移动端IM实践:Android版微信如何大幅提升交互性能(一)》
《移动端IM实践:Android版微信如何大幅提升交互性能(二)》
《移动端IM实践:WhatsApp、Line、微信的心跳策略分析》
《移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信)》
《信鸽团队原创:一起走过 iOS10 上消息推送(APNS)的坑》
>> 更多同类文章 ……
[2] 有关QQ、微信的技术故事:
《2017微信数据报告:日活跃用户达9亿、日发消息380亿条》
《技术往事:创业初期的腾讯——16年前的冬天,谁动了马化腾的代码》
《技术往事:史上最全QQ图标变迁过程,追寻IM巨人的演进历史》
《开发往事:深度讲述2010到2015,微信一路风雨的背后》
《开发往事:记录微信3.0版背后的故事(距微信1.0发布9个月时)》
>> 更多同类文章 ……
(本文同步发布于:http://www.52im.net/thread-1461-1-1.html)
微信团队分享:iOS版微信的高性能通用key-value组件技术实践的更多相关文章
- 微信团队分享:iOS版微信是如何防止特殊字符导致的炸群、APP崩溃的?
本文来自微信开发团队yanyang的技术分享. 1.引言 相信大家都遇到过一段特殊文本可以让iOS设备所有app闪退的经历.前段时间大年初一,又出现某个印度语字符引起iOS11系统奔溃,所幸iOS版微 ...
- 微信团队原创分享:iOS版微信的内存监控系统技术实践
本文来自微信开发团队yangyang的技术分享. 一.前言 FOOM(Foreground Out Of Memory),是指App在前台因消耗内存过多引起系统强杀.对用户而言,表现跟crash一样. ...
- 微信团队分享:极致优化,iOS版微信编译速度3倍提升的实践总结
1.引言 岁月真是个养猪场,这几年,人胖了,微信代码也翻了. 记得 14 年转岗来微信时,用自己笔记本编译微信工程才十来分钟.如今用公司配的 17 年款 27-inch iMac 编译要接近半小时:偶 ...
- 微信团队分享:Kotlin渐被认可,Android版微信的技术尝鲜之旅
本文由微信开发团队工程是由“oneliang”原创发表于WeMobileDev公众号,内容稍有改动. 1.引言 Kotlin 是一个用于现代多平台应用的静态编程语言,由 JetBrains 开发( ...
- 腾讯微信被怼,iOS版微信不能打赏了
2017年4月19日,估计很多有着大量粉丝的微信自媒体作者会感到很不爽,因为他们的苹果粉丝再也无法很爽快地.肆意.任性地打赏他们了,按目前iphone手机的占有率,估计打赏率会掉一半以上. 据微信派微 ...
- 通过微信Android和iOS版,看两大系统的差异
由于设计师或者产品经理使用的移动设备大部分是iPhone,所以在做设计时,容易忽略Android和iOS的差异,按照自己的使用习惯进行设计,导致大部分设计师或产品经理做出的设计都是基于iOS规范或习惯 ...
- iOS版微信6.5.21发布 适配iPhone X
昨日,iOS版微信迎来v6.5.21正式版发布,本次升级主要适配iPhone X,在聊天中查找聊天内容时,可以查找交易消息.可以给聊天中的消息设置日期提醒.上一个正式版v6.5.16发布于9月13日, ...
- 解决alert在ios版微信中显示url的问题(重写alert)
为了解决alert在ios版微信中显示url的问题 window.alert = function(name){ var iframe = document.createElement("I ...
- 微信JSSDK分享朋友圈微信自定义分享接口
服务项目 新手技术咨询 企业技术咨询 定制开发 服务说明 QQ有问必答 QQ.微信.电话 微信开发.php开发,网站开发,系统定制,小程序开发 价格说明 200元/月 1000/月 商议 ...
随机推荐
- GIT指令简洁篇
查看.添加.提交.删除.找回,重置修改文件 git help <command> # 显示command的help git show # 显示某次提交的内容 git show $id gi ...
- Selenium2+python自动化49-判断文本(text_to_be_present_in_element)【转载】
前言 在做结果判断的时候,经常想判断某个元素中是否存在指定的文本,如登录后判断页面中是账号是否是该用户的用户名. 在前面的登录案例中,写了一个简单的方法,但不是公用的,在EC模块有个方法是可以专门用来 ...
- Python timedelta模块 时间增减用法
timedalte 是datetime中的一个对象,该对象表示两个时间的差值 构造函数:datetime.timedelta(days=0, seconds=0, microseconds=0, mi ...
- SnowNLP:一个处理中文文本的 Python 类库[转]
SnowNLP是一个python写的类库,可以方便的处理中文文本内容,是受到了TextBlob的启发而写的,由于现在大部分的自然语言处理库基本都是针对英文的,于是写了一个方便处理中文的类库,并且和Te ...
- codeforces 869A The Artful Expedient【暴力枚举/亦或性质】
A. time limit per test 1 second memory limit per test 256 megabytes input standard input output stan ...
- 并查集【p2700】逐个击破
题目描述-->p2700 逐个击破 题意概括 花费最小的代价,使得一些有标记的节点不连通. 分析 我们需要花费最小代价使得原来连通的图中一些节点之间不相互连通. 贪心显然是可行的(一点也不显然 ...
- 【动态规划】矩形嵌套 (DGA上的动态规划)
[动态规划]矩形嵌套 时间限制: 1 Sec 内存限制: 128 MB提交: 23 解决: 9[提交][状态][讨论版] 题目描述 有n个矩形,每个矩形可以用a,b来描述,表示长和宽.矩形X(a, ...
- 12、Django实战第12天:课程机构列表页数据展示
今天完成的是课程机构列表页.... 1.启动服务,进入xadmin后,添加5个城市信息用作测试数据 2.添加课程机构,其中有一项要上传封面图的地方要注意 封面图上传路径是我们在models中设置好的 ...
- 将千克转换成磅 Exercise05_03
/** * @author 冰樱梦 *题目:将千克转换成磅 *时间:2018年下半年 */ public class Exercise05_03 { public static void main(S ...
- Java中泛型得到T.class
例子: public class Test<T> { public Class<T> getTClass() { return (Class<T>) ((Param ...