
作為互聯網通信云獨角獸的融云每天要存儲的消息量高達數十億條,多年來融云一直致力于消息存儲的優(yōu)化,從原型階段的MySQL到后來的Redis、LevelDB,融云不停的探索實踐。隨著業(yè)務的發(fā)展和數據的持續(xù)增長,融云需要一個既能滿足業(yè)務需求,又能滿足大業(yè)務量的消息數據存儲,因此融云研究院在2017年決定研發(fā)可以滿足自身業(yè)務特點的高性能消息存儲服務(內部代號RCTSDB),并使用全新設計的數據存儲引擎。
以下內容摘自李淼演講實錄。
融云消息存儲歷程
首先是融云在開始時的原型產品驗證階段,大概是在2013年初創(chuàng)階段,為了驗證融云的即時通信業(yè)務模式,此時的消息都是存儲在MySQL中,其特點是開發(fā)簡單,可以滿足各種產品需求。
在原型驗證通過后,正式上線前融云將離線消息遷移到了Redis中以滿足性能需求,而歷史消息則繼續(xù)保存在MySQL中。
融云經過一年多業(yè)務飛速的發(fā)展,要存儲的消息越來越多,而Redis集群也幾乎每1-2個月就要進行擴容。當時處于對成本的考量,融云決定采用相對低廉的磁盤存儲方案。此時融云做了很多選型,最終決定采用基于levelDB作為存儲引擎并自研DB。但是當時的由于levelDB數據歸并消耗高,數據淘汰困難等問題,運行兩個月后替換了原來的Redis存儲方案。
目前融云的線上情況是Redis存儲離線消息,levelDB存儲歷史消息,而融云的業(yè)務也相對進入了平穩(wěn)期,Redis最近一次擴容是在2018年的5、6月份,根據業(yè)務增速情況可以支持到2018年底。
存儲架構相對穩(wěn)定,為什么融云還要啟動自研存儲項目呢?
滿足一些復雜的業(yè)務場景需求
基于目前的存儲方案,一些需求實現起來非常困難,而這些需求都是來自客戶,從而制約產品的演進,所以融云急需一個替代方案;
降低整體的成本投入
融云線上的Redis集群成本是所有設備投入的一半以上,對于存儲的優(yōu)化,顯然是可以持續(xù)降低公司運營成本;
簡化部署模型
對于Redis的部署不是很復雜,但是融云除了公有云的業(yè)務以外還有私有云項目,繼續(xù)使用Redis對客戶側的運維部署成本就會變的很高;
源碼可控
之前融云使用過很多的開源產品,當這些產品不能滿足業(yè)務需求時,融云又急需某些特性時,這就需要和作者聯系,但是大部分時候作者都不能及時響應或者根本不在其計劃內,而這時融云只能等或者自己改,自己改的又回饋不了開源產品的主干上,或者當開源產品更新沒辦法合并,這樣就迫使融云必須啟動自研存儲項目。
即時通信類產品,自研存儲需具備哪些特點?
快速的數據淘汰能力
數據淘汰的過程不能對系統產生任何的影響;
避免數據合并
相對于levelDB來講,當寫入很多操作的時候levelDB的數據合并經常會發(fā)生CPU報警,導致寫入查詢響應速度慢等情況;
讀寫性能要求高
至少不能比融云現有使用的Redis速度慢;
開發(fā)使用靈活
在融云存儲引擎設計過程中,不僅只是存儲數據,而是當作開發(fā)框架來進行設計的,在各操作點上都提供Hook,從而能夠滿足各種業(yè)務場景需求。
站在前人的肩膀上遠眺
融云在存儲引擎設計過程中借鑒很多已有的成熟方案,并將這些方案進行優(yōu)化整合,最終完成了自有的引擎設計。下面將羅列一些方案,并向前人致敬。
數據寫入采用WAL模式
數據在寫入內存時同時記錄,當服務宕機或重啟的時候可以根據這些恢復內存數據。這些都是按照磁盤順序寫入,可以變相的提高存儲引擎性能。一般主流的數據庫都會采用這種模式完成數據寫入。
借鑒InfluxDB中的LSM數據結構
LSM數據結構是目前一些新興數據庫采用的數據結構,像LevelDB、RocksDB、HBase、Cassandra等。即時通訊消息具備時序數據的特點,而InfluxDB更是時序數據庫中的佼佼者,融云對InfulxDB做了一些改造,使其更適合存儲一些時序數據
借鑒whiskey 的 K / V 分離存儲設計
whiskey 是2016年發(fā)表的一篇論文,主要解決了LSM中大數量寫入后頻繁數據歸并的問題,在LSM這種數據結構中,數據的Key和Value值都是要寫入內存的,當數據到達內存設定的閾值時進行歸檔處理。對于value值較大的數據來說,這個歸檔就會變得特變頻繁,而whiskey的理念是將value單獨保存至另外的文件位置,LSM結構內保存的是Key以及這個Value所在文件的偏移量和長度,以此來降低歸檔頻。按照論文上的介紹,歸檔頻率可以降低一個數量級。
借鑒MyISAM的存儲文件設計
在文件設計這塊融云一共經歷四版改動,最終殊途同歸。融云發(fā)現Mysql中MyISAM引擎的文件設計很有類似之處。
融云消息存儲引擎設計
1.存儲邏輯劃分

2.存儲文件規(guī)劃
關于Table文件分為三種文件進行組織存儲:
- xxx.data 數據存儲文件;
- xxx.index 數據索引文件;
- xxx.info table信息文件。
文件并沒有按照Table文件進行劃分存儲,是按照序號字段進行排序,為的是在設計過程中解決主從復制,提高便捷性。
3.數據寫入邏輯

4.數據文件設計

5.日志文件設計

6.索引文件設計

7.信息文件設計

內存優(yōu)化
- 在M_block中融云重度依賴跳表這種數據結構,融云的存儲引擎是用java寫的,主要考慮是后面可移植的問題。起初融云采用了java里面內置的ConcurrentSkipList,但是其內存消耗很高,這個主要是java的中對象內存分配的規(guī)則導致的。所以融云重寫了SkipList,放棄了java中的對象模式。重新造的輪子其內存消耗只有原始 1/4,同時也犧牲了一些東西,例如:刪除跳表內的數據時,其刪除的數據所占的內存無法釋放,但是對于即時通信消息來講基本上不存在刪除的場景,同樣一些時序數據也極少存在刪除場景;
- 索引數據融云進行了一系列緊湊處理。優(yōu)化后40億級的索引數據,只消耗內存400MB。放棄java對象模式,直接采用byte數值的方式進行數據組織;
- 對很多的對象又做了一些細節(jié)處理,想辦法把Java本身的一些內存模型給抹平掉,通過這種方式來降低內存利用率;
- 最后融云做了LIRS的緩存機制。
存儲優(yōu)化
- 索引數據前綴壓縮,降低磁盤的寫入量;
- 數值數據采用VarInt編碼;
- 業(yè)務數據QuickLZ壓縮,平衡了存儲及CPU的使用率;
- 數據寫入采用雙循環(huán)可變長度Buffer,使數據寫入過程中是沒有直接操作的,有效降低延遲的產生;
- 重復數據引用寫入,該優(yōu)化對于即時通信場景有顯著成效。
服務端架構
該架構主要包含Broker,以及一些數據的分組Master、Slaver,這些數據是根據ZooKeeper進行管理,同時向Broker進行匯報。在Broker上會開設不同的端口去設置各種不同的協議。最后是DB manger,主要是用于管理引擎的各種數據查詢的插件,就像前文提到的該引擎除了是用于數據存儲外還是開發(fā)框架,程序員在架構上可以靈活按照熟悉的開發(fā)語言去直接操作這些數據。

數據存儲引擎項目將在年底開源
李淼在會上表示,為了促進產業(yè)內的技術交流,融云會在未來兩個月時間對數據存儲引擎項目進行開源,開源前除了對引擎做一些優(yōu)化以外,還會補充一些相關的文檔,同時為了方便開發(fā)者集成參考還會對代碼增加一些注釋。項目開源后意味著融云是國內首家將自研的消息存儲引擎開源的云通信廠商,也正在為中國的開源環(huán)境貢獻自己應盡的力量。
關于融云:融云,安全、可靠的全球互聯網通信云服務商,向開發(fā)者和企業(yè)提供即時通訊和實時音視頻通信云服務,據艾瑞等權威數據顯示,融云即時通訊云業(yè)務市場份額穩(wěn)居第一。目前,已有數十萬互聯網用戶及上千家企業(yè)級用戶通過融云實現了場景化溝通,并從中獲益,包括招商銀行、工商銀行、交通銀行、民生銀行、中國移動、四川航空、CCTV微視、中聯重科、58 趕集、大河報業(yè)、新東方、陸金所、易車網、豬八戒、蔚來汽車、得到APP、荔枝 FM、汽車之家、優(yōu)酷來瘋、攜程愛玩、聚力視頻、百姓網等知名企業(yè)及應用。