本文來自微信開發團隊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)