本文来自微信开发团队guoling的技术分享。php
本文要分享的是iOS版微信内部正在推广和使用的一个高性能通用key-value 组件的技术实践过程,该组件在微信内部被命名为MMKV(如下简称MMKV)。html
MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。但愿对于有高性能key-value 组件或相似技术需求的IM同行,能经过本文得到必定的启发。git
学习交流:程序员
- 即时通信开发交流群:320837163[推荐]数据库
- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM》安全
(本文同步发布于:http://www.52im.net/thread-1461-1-1.html)微信
在 iOS 微信的平常运营中,时不时就会爆发特殊文字引发 iOS 系统的 crash,《微信团队分享:iOS版微信是如何防止特殊字符致使的炸群、APP崩溃的?》一文里面设计的技术方案是在关键代码先后进行计数器的加减,经过检查计数器的异常,来发现引发闪退的异常文字。网络
《微信团队分享:iOS版微信是如何防止特殊字符致使的炸群、APP崩溃的?》里设计的技术方案大体原理就是:微信开发
1)在会话列表、会话界面等有大量 cell 的地方,但愿新加的计时器不会影响滑动性能;架构
2)这些计数器还要永久存储下来——由于闪退随时可能发生。
这就须要一个性能很是高的通用 key-value 存储组件,咱们考察了 NSUserDefaults、SQLite 等常见组件,发现都没能知足如此苛刻的性能要求。考虑到这个防 crash 方案最主要的诉求仍是实时写入,而 mmap 内存映射文件恰好知足这种需求,咱们尝试经过它来实现一套 key-value 组件。
经过 mmap 内存映射文件,提供一段可供随时写入的内存块,App 只管往里面写数据,由 iOS 负责将内存回写到文件,没必要担忧 crash 致使数据丢失。
数据序列化方面咱们选用 protobuf 协议,pb 在性能和空间占用上都有不错的表现。考虑到咱们要提供的是通用 kv 组件,key 能够限定是 string 字符串类型,value 则多种多样(int/bool/double等)。要作到通用的话,考虑将 value 经过 protobuf 协议序列化成统一的内存块(buffer),而后就能够将这些 KV 对象序列化到内存中。
更多有关Protobuf的文章请见:
《强列建议将Protobuf做为你的即时通信应用数据传输格式》
《全方位评测:Protobuf性能到底有没有比JSON快5倍?》
《一个基于Protocol Buffer的Java代码演示》
《详解如何在NodeJS中使用Google的Protobuf》
标准 protobuf 不提供增量更新的能力,每次写入都必须全量写入。
考虑到主要使用场景是频繁地进行写入更新,咱们须要有增量更新的能力:
1)将增量 kv 对象序列化后,直接 append 到内存末尾;
2)这样同一个 key 会有新旧若干份数据,最新的数据在最后;
3)那么只需在程序启动第一次打开 mmkv 时,不断用后读入的 value 替换以前的值,就能够保证数据是最新有效的。
使用 append 实现增量更新带来了一个新的问题,就是不断 append 的话,文件大小会增加得不可控。例如同一个 key 不断更新的话,是可能耗尽几百 M 甚至上 G 空间,而事实上整个 kv 文件就这一个 key,不到 1k 空间就存得下。这明显是不可取的。
咱们须要在性能和空间上作个折中:
1)之内存 pagesize 为单位申请空间,在空间用尽以前都是 append 模式;
2)当 append 到文件末尾时,进行文件重整、key 排重,尝试序列化保存排重结果;
3)排重后空间仍是不够用的话,将文件扩大一倍,直到空间足够。
考虑到文件系统、操做系统都有必定的不稳定性,咱们另外增长了 crc 校验,对无效数据进行甄别。在 iOS 微信现网环境上,咱们观察到有平均约 70w 日次的数据校验不经过。
MMKV 提供一个全局的实例,能够直接使用:
能够看到,MMKV 在使用上仍是比较简单的。若是不一样业务须要区别存储,也能够单首创建本身的实例:
支持如下 C 语语言基础类型:
bool、int3二、int6四、uint3二、uint6四、float、double
支持如下 ObjC 类型:
NSString、NSData、NSDate
写了个简单的测试,将 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,本文略有改动)
[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)