微信团队分享: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 对象序列化到内存中。

微信团队分享:iOS版微信的高性能通用key-value组件技术实践_第1张图片

更多有关Protobuf的文章请见:

《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)排重后空间还是不够用的话,将文件扩大一倍,直到空间足够。

微信团队分享:iOS版微信的高性能通用key-value组件技术实践_第2张图片

3.5 数据有效性

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

4、MMKV 使用

4.1 快速上手

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

微信团队分享:iOS版微信的高性能通用key-value组件技术实践_第3张图片

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

微信团队分享:iOS版微信的高性能通用key-value组件技术实践_第4张图片

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)。

微信团队分享:iOS版微信的高性能通用key-value组件技术实践_第5张图片

可见 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的线程死锁监控系统技术实践》

《微信团队原创分享:iOS版微信的内存监控系统技术实践》

《让互联网更快:新一代QUIC协议在腾讯的技术实践分享》

《iOS后台唤醒实战:微信收款到账语音提醒技术总结》

《腾讯技术分享:社交网络图片的带宽压缩技术演进之路》

《微信团队分享:视频图像的超分辨率技术原理和应用场景》

《微信团队分享:微信每日亿次实时音视频聊天背后的技术解密》

《QQ音乐团队分享:Android中的图片压缩技术详解(上篇)》

《QQ音乐团队分享:Android中的图片压缩技术详解(下篇)》

《腾讯团队分享:手机QQ中的人脸识别酷炫动画效果实现详解》

《腾讯团队分享 :一次手Q聊天界面中图片显示bug的追踪过程分享》

《微信团队分享:微信Android版小视频编码填过的那些坑》 

《微信手机端的本地数据全文检索优化之路》 

《企业微信客户端中组织架构数据的同步更新方案优化实战》

《微信团队披露:微信界面卡死超级bug“15。。。。”的来龙去脉》

《QQ 18年:解密8亿月活的QQ后台服务接口隔离技术》

《月活8.89亿的超级IM微信是如何进行Android端兼容测试的》

《以手机QQ为例探讨移动端IM中的“轻应用”》

《一篇文章get微信开源移动端数据库组件WCDB的一切!》

《微信客户端团队负责人技术访谈:如何着手客户端性能监控和优化》

《微信后台基于时间序的海量数据冷热分级架构设计实践》

《微信团队原创分享:Android版微信的臃肿之困与模块化实践之路》

《微信后台团队:微信后台异步消息队列的优化升级实践分享》

《微信团队原创分享:微信客户端SQLite数据库损坏修复实践》 

《腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率》 

《腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(下篇)》 

《腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(上篇)》 

《微信Mars:微信内部正在使用的网络层封装库,即将开源》 

《如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源》 

《开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石 [源码下载]》 

《微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解》 

《微信团队原创分享:Android版微信后台保活实战分享(进程保活篇)》 

《微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)》 

《Android版微信从300KB到30MB的技术演进(PPT讲稿) [附件下载]》 

《微信团队原创分享:Android版微信从300KB到30MB的技术演进》 

《微信技术总监谈架构:微信之道——大道至简(演讲全文)》

《微信技术总监谈架构:微信之道——大道至简(PPT讲稿) [附件下载]》 

《如何解读《微信技术总监谈架构:微信之道——大道至简》》

《微信海量用户背后的后台系统存储架构(视频+PPT) [附件下载]》

《微信异步化改造实践:8亿月活、单机千万连接背后的后台解决方案》 

《微信朋友圈海量技术之道PPT [附件下载]》 

《微信对网络影响的技术试验及分析(论文全文)》 

《一份微信后台技术架构的总结性笔记》 

《架构之道:3个程序员成就微信朋友圈日均10亿发布量[有视频]》 

《快速裂变:见证微信强大后台架构从0到1的演进历程(一)》

《快速裂变:见证微信强大后台架构从0到1的演进历程(二)》 

《微信团队原创分享:Android内存泄漏监控和优化技巧总结》 

《全面总结iOS版微信升级iOS9遇到的各种“坑”》 

《微信团队原创资源混淆工具:让你的APK立减1M》 

《微信团队原创Android资源混淆工具:AndResGuard [有源码]》 

《Android版微信安装包“减肥”实战记录》 

《iOS版微信安装包“减肥”实战记录》 

《移动端IM实践:iOS版微信界面卡顿监测方案》 

《微信“红包照片”背后的技术难题》 

《移动端IM实践:iOS版微信小视频功能技术方案实录》 

《移动端IM实践:Android版微信如何大幅提升交互性能(一)》

《移动端IM实践:Android版微信如何大幅提升交互性能(二)》

《移动端IM实践:实现Android版微信的智能心跳机制》 

《移动端IM实践:WhatsApp、Line、微信的心跳策略分析》 

《移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信)》

《移动端IM实践:iOS版微信的多设备字体适配方案探讨》 

《信鸽团队原创:一起走过 iOS10 上消息推送(APNS)的坑》

《腾讯信鸽技术分享:百亿级实时消息推送的实战经验》

>> 更多同类文章 ……

[2] 有关QQ、微信的技术故事:

《QQ和微信凶猛成长的背后:腾讯网络基础架构的这些年》

《闲话即时通讯:腾讯的成长史本质就是一部QQ成长史》

《2017微信数据报告:日活跃用户达9亿、日发消息380亿条》

《腾讯开发微信花了多少钱?技术难度真这么大?难在哪?》

《技术往事:创业初期的腾讯——16年前的冬天,谁动了马化腾的代码》 

《技术往事:史上最全QQ图标变迁过程,追寻IM巨人的演进历史》 

《技术往事:“QQ群”和“微信红包”是怎么来的?》 

《开发往事:深度讲述2010到2015,微信一路风雨的背后》 

《开发往事:微信千年不变的那张闪屏图片的由来》 

《开发往事:记录微信3.0版背后的故事(距微信1.0发布9个月时)》 

《一个微信实习生自述:我眼中的微信开发团队》

《首次揭秘:QQ实时视频聊天背后的神秘组织》

>> 更多同类文章 ……

你可能感兴趣的:(微信团队分享:iOS版微信的高性能通用key-value组件技术实践)