網易雲背後的數據庫:Facebook開源,完全兼容MySQL

MyRocks是完全兼容MySQL的一個關係型數據庫系統,由Facebook於2016年下半年開源,網易杭研數據庫內核團隊在2018年初引入,在網易雲音樂某些業務場景落地使用,取得了較好的效果,在這之後落地了更多地業務場景。

自己整理的Java架構學習視頻和大廠項目底層知識點,需要的同學歡迎私信我【資料】發給你~一起學習進步!

簡述

MyRocks是Facebook數據庫工程團隊將MySQL的默認存儲引擎InnoDB(基於B+樹)替換為自家的RocksDB(基於LSM樹)後發展而來的MySQL分支版本,其繼承了MySQL的SQL語法,數據高可用框架和配套工具。

MyRocks具有較高的普適性,對於MySQL用戶來說,將MySQL替換為MyRocks是透明的,在使用方面並沒有差別。最大的不同是DML操作(數據操作語句,如增、刪、改)性能相比MySQL有明顯優勢;同樣的數據量,所需的存儲空間明顯變少。

網易雲背後的數據庫:Facebook開源,完全兼容MySQL

目前MyRocks在雲音樂內使用,業務場景包括雲音樂實時推薦、離線推薦、歷史聽歌記錄、P0核心庫延遲從庫和雲計算NOS系統內部數據管理等等,累計節省了百萬級的成本開銷。


項目介紹

網易雲背後的數據庫:Facebook開源,完全兼容MySQL

MyRocks是MySQL和RocksDB的結合,MySQL是目前最流行的開源關係型數據庫系統,RocksDB是Facebook在Google的LevelDB基礎上改進的KV存儲引擎。

RocksDB簡介

RocksDB基於Log Structured Merge Tree(LSM樹)存儲數據,下面簡單介紹下RocksDB。

網易雲背後的數據庫:Facebook開源,完全兼容MySQL


如上圖所示。在RocksDB中,LSM可有多層,圖中為4層(L0~L3),每一層都會獨立進行Compaction操作;可以為每個用戶數據表/索引創建一個列族(CF),每個CF都是一顆LSM樹;每顆LSM樹都會有數個Write Buffer(MemTable),用於緩存已經提交的事務數據;事務提交時會將WAL(Write Ahead Log)持久化到全局的日誌文件中。

網易雲背後的數據庫:Facebook開源,完全兼容MySQL


每個事務都有一個writebatch對象,用於緩存該事務在提交前修改的所有數據。在事務提交時,其修改的數據先寫入WAL日誌buffer,根據配置參數選擇是否持久化。然後將修改的數據有序寫入到Active MemTable中。當Active MemTable達到閾值後會變為Immutable,再新產生一個Active MemTable。

數據寫入MemTable後就意味著事務已經提交。數據的持久化和Compaction都是異步進行的。當Immutable MemTable個數達到所設置的參數閾值後,會被回刷成L0 SST文件。在L0文件個數達到閾值後,合併到L1上並依次往下刷。RocksDB中可以配置多個線程用於對每層數據文件進行Compaction。

由於其優秀的特點,目前已經被集成到MySQL(即MyRocks)、MongoDB(即MongoRocks)、TiDB/TiKV和Nebula等關係型數據庫、文檔數據庫、分佈式KV、NewSQL和圖數據庫中,成為實際上的KV存儲引擎標準。

【MyRocks優缺點】

MyRocks繼承了MySQL優秀的基因,包括完全兼容的MySQL SQL語法、行級鎖粒度、讀已提交(RC)和可重複讀(RR)兩種事務隔離級別、多版本併發控制(MVCC,Multi-Version ConCurrent Control)、成熟的主從複製框架和完善的配套工具等功能和特性。

核心競爭力

由於引入RocksDB,MyRocks具備比MySQL(相比基於B+樹的InnoDB)更多優點,如更快的寫入速度,更小的存儲空間,更高的壓縮效率等;這是MyRocks的核心競爭力,下面簡單對比說明。

  • 存儲效率高,寫性能好
網易雲背後的數據庫:Facebook開源,完全兼容MySQL

InnoDB是典型的B/B+樹存取方式,即使是順序插入場景,也會在一個page還未完全填滿時觸發分裂,變為2個半空的page,也就是說InnoDB的page一定存在頁內碎片。
而RocksDB由於是追加(Append)方式,所以都是文件順序寫行為;MemTable從內存 Flush到磁盤後成為獨立的sst文件,不同的sst文件間的Merge操作也是順序讀寫,整個過程均不會產生內部碎片。

  • 存儲空間小
網易雲背後的數據庫:Facebook開源,完全兼容MySQL

即使在非壓縮模式下,RocksDB記錄也進行了前綴壓縮/編碼,默認每16條記錄才有一條是完整的,節省了後續15條記錄的共同前綴所需的存儲空間。
此外,RocksDB每個索引佔用7+1 bytes(seq id + flag)的元數據開銷,而InnoDB每記錄6+7 bytes (trx_id + undo ptr)空間開銷,似乎在多個索引的場景下RocksDB更加費空間,但Ln SST文件seq id可置0,經過壓縮等操作後絕大部分元數據開銷是可節省的,只需佔用1byte,因此RocksDB的元數據開銷也比InnoDB更省。

  • 壓縮效率高
網易雲背後的數據庫:Facebook開源,完全兼容MySQL


在壓縮效率方面,也是RocksDB更佔據優勢。InnoDB壓縮以page/block為單位來獨立保持壓縮後的數據,而文件系統是需要塊對齊的,一般都是4KB,這就意味著一個16KB的頁,即使壓縮為5KB,也需要使用2個文件block,即8K保存。其中的3KB空間填空。

RocksDB很好得解決了這個問題。每個block壓縮後,先組合成一個SST文件,保存時只需要SST文件對齊到4KB即可。

不足

在性能上,MyRocks相比InnoDB不足之處在於查詢性能(主要是範圍查詢),在這方面,MyRocks通過使用布隆過濾器(Bloom Filter)進行了查詢優化。

相對來說,MyRocks作為一個年輕的開源項目,相比MySQL/InnoDB在成熟度上和功能完整度上有所欠缺,包括XA事務支持度不夠、在線DDL性能不足且內存消耗過大、支持的索引類型偏少(無地理位置索引、全文索引等)等。此外,Bug數也更多。


【使用場景和實際效果】

由於MyRocks完全兼容MySQL,因此,對於一般的,比如讀寫壓力都不大、只需關係型數據庫基本功能的業務場景下,MyRocks是完全可以替代MySQL的,但這不是MyRocks項目的存在意義,因為帶來不了多少價值。

MyRocks應該用在相比現有方案能夠明顯產生更多價值的場景,下面列舉MyRocks典型的應用場景,並介紹在部分場景上的使用效果。

大數據量場景

這類場景典型的特點是數據量大,此外可能數據增長也很快,也可能有過期刪除的需求等,這類場景包括用戶行為,動態等。我們曾與DBA對雲音樂和考拉等業務的MySQL實例進行應用場景分析,發現這類場景的MySQL實例佔有不小的比率。

MyRocks由於具備更高地壓縮比,可以極大縮小數據的存儲空間。降低這部分業務的存儲開銷。而且MyRocks支持數據TTL(Time To Live)特性,可以設定數據過期時間,更加方便進行數據生命週期管理。

目前雲音樂有多個這類業務場景使用了MyRocks,這裡拿雲音樂的用戶歷史聽歌記錄為例。

網易雲背後的數據庫:Facebook開源,完全兼容MySQL


在沒替換為MyRocks前,歷史聽歌記錄為一個下掛有16個MySQL高可用實例的DDB集群。數據採用InnoDB壓縮表形式存儲,每個節點數據約1TB,整個集群共32TB,如上圖所示。下圖為替換為MyRocks後的情況,設置RocksDB的壓縮算法為Snappy,單個節點的數據量降為322GB,節省了700GB。整個集群可省下20TB SSD盤,節省了超過2/3的存儲成本;由於計算資源利用率低,存儲縮小後,可提高物理服務器的MyRocks實例部署密度,進一步節省計算資源;同時數據增長速度也得到控制,集群擴容週期變長

寫密集型場景

這類場景的特點是數據量不小,對寫性能要求超過了MySQL/InnoDB的能力,且對讀的延遲和平穩性有較高要求。如果僅需高寫入能力,那麼可選擇分佈式KV系統,比如HBase,其提供了不弱於MyRocks的寫入能力,且擴展性更好。但若對讀性能有較高要求,那麼選擇MyRocks會更好,因為基於C++實現的MyRocks沒有Java的GC等問題,性能更加平穩。

如果是數據量較小,且對讀寫均有較高要求的場景,可採用Redis等緩存方案。但若業務場景的數據量較大,使用緩存的方案成本就會過高。

這類場景有推薦類、業務流水和日誌類等等。在網易公司內部各部門均廣泛存在類似的場景,目前雲音樂的實時推薦、離線推薦等,NOS內部數據反查服務等均已使用MyRocks,這裡舉雲音樂實時推薦為例。

網易雲背後的數據庫:Facebook開源,完全兼容MySQL

在本案例中主要還是關心成本。因為數據量比較大,需要大量的內存,而且隨著推薦的實時化改進和推薦場景的增多,內存使用成本急劇增加,甚至可能出現內存採購來不及的情況。

顯然,單個MyRocks肯定扛不住全部的壓力,於是仍採用DDB+MyRocks的方式將壓力拆分到多個實例上。但落到MyRocks的寫入壓力仍較大,雖MyRocks主庫能夠扛住,但由於MySQL主從複製跟不上,所以MyRocks從庫延遲不斷增加。由於推薦類業務對數據一致性有一定的容忍度,所以,最終落地的方案是業務層雙寫2個MyRocks而不是MyRocks層做主從複製。下圖為當前的部署架構。

網易雲背後的數據庫:Facebook開源,完全兼容MySQL

圖中分左右兩部分,左邊為8個MyRocks節點組成的DDB1集群,右邊也是8個MyRocks組成的DDB2集群,推薦算法邏輯相關的單元用Flink來表示。算法單元從DDB1讀取上一週期的推薦數據,跟當前實時變化的新數據相結合計算出本週期的推薦數據,然後將其分別寫入DDB1和DDB2。推薦業務使用方從DDB2上讀取所需的推薦數據。

網易雲背後的數據庫:Facebook開源,完全兼容MySQL

網易雲背後的數據庫:Facebook開源,完全兼容MySQL

上圖為經過參數調優後的MyRocks節點性能表現,DDB1上的節點讀寫QPS/TPS達到1.5w/s,DDB2上的業務讀QPS達到0.5w/s,讀延遲大部分時間均在2ms以下,平穩度高,波動小。

總的來說,實時推薦通過將Redis替換為MyRocks集群,在性能上扛住了業務負載的情況下有效降低了所需投入的硬件成本。

其他適用場景

依託高寫入性能和低存儲開銷的特點,MyRocks還可作為MySQL高可用實例的低成本從庫,比如防止數據誤刪的延遲從庫(比線上數據延遲n個小時),用於數據導入的專用從庫等。目前雲音樂的歌單、用戶和曲庫等所有P0級的MySQL核心庫,均建立了延遲從庫,這些節點都部署在同一個物理機服務器上,每個節點僅需5G內存,原數據量1/3以下的存儲空間,如下圖所示。基於MyRocks的延遲從庫以非常低的成本換來了核心業務數據更高的安全保障。

網易雲背後的數據庫:Facebook開源,完全兼容MySQL

除了上面所述的場景外,還有更多其他場景可以使用MyRocks,在此不一一展開說明。


【使用心得】

當然,MyRocks在落地使用過程並不是一帆風順的。在新的場景類型落地時遇到了不少問題,杭研的數據庫內核團隊和杭研DBA、業務開發一道為最終的推廣使用做了至關重要的貢獻,包括對MyRocks進行功能增強、BugFix、參數調優和使用經驗輸出等。下面舉例介紹。

1.問題與優化

在使用過程中,我們發現、定位和解決了數十個大大小小的問題,對MyRocks進行優化增強來滿足業務需求。先後發佈了3個MyRocks內核版本,其中第一個版本將開源的MyRocks代碼合入杭研MySQL分支InnoSQL中,後兩個版本(v4a和v4b)均是對MyRocks的優化增強和BugFix。

InnoSQL 5.7.20-v4a:

網易雲背後的數據庫:Facebook開源,完全兼容MySQL

InnoSQL 5.7.20-v4b:

網易雲背後的數據庫:Facebook開源,完全兼容MySQL

2.MyRocks XA事務增強

這裡要展開說的是我們為MyRocks增加了完整的XA事務處理能力,這是MyRocks想要在網易內部大規模使用所必備的能力。因為網易眾多業務均使用DDB作為MySQL分庫分表的解決方案,而DDB可能會大量產生XA事務。
開源版MyRocks僅支持執行XA事務,但不支持回放XA事務,因為XA事務PREPARE後對應Session不能執行除XA COMMIT或XA ROLLBACK外其他操作,這在主庫是沒有問題的,可以通過創建新的Session執行其他事務操作,但在Slave端,用於回放事務的worker線程(等同於主庫的Session)是有限的。這種情況下,也就是說如果存在XA事務,那麼就無法使用MyRocks高可用實例,其原因是MySQL的XA事務複製框架與存儲引擎強相關。

網易雲背後的數據庫:Facebook開源,完全兼容MySQL

MyRocks並沒有適配這套框架,如在XA START和PREPARE時必須的detach/reattach操作等,杭研數據庫內核團隊參考InnoDB實現了MyRocks的XA事務相關API接口。
除了實現API接口外,由於目前MySQL官方僅有InnoDB一個支持事務的存儲引擎,所以MySQL對多存儲引擎混合的XA事務支持上有較大問題,最典型的問題是多種不同原因導致的內存洩露;此外,我們發現MyRocks的XA PREPARE並未進行WAL持久化,會導致mysqld crash後數據丟失。這些問題均通過源碼級分析完成問題定位並得到了有效解決。

3.參數調優

RocksDB有數百個配置參數,其中暴露到MySQL上的有100+個。這些參數影響了MyRocks內存使用、MyRocks性能和數據持久性等方方面面,需要根據不同的業務場景進行最優化配置。

合理配置內存相關參數

MyRocks與內存相關的參數較多,如block cache、MemTable和CF相關配置等。設置過小可能影響寫性能,設置過大可能導致OOM。

網易雲背後的數據庫:Facebook開源,完全兼容MySQL


在雲音樂歷史聽歌記錄場景下,我們本來採用每個用戶數據表一個CF的配置方案。由於每個CF均有獨立的MemTable,會佔據幾百MB甚至數GB的內存空間,而該場景下每個MyRocks實例有較多數據表,出現因為MemTable佔據內存過多而OOM的情況。所以需要合理規劃CF個數,每個CF的MemTable總大小由於max_write_buffer_size,max_write_buffer_number*等4個參數共同決定,其大小會影響該CF的寫性能,應根據業務場景進行合理配置。
除了參數調優,在內存使用方面,我們還進行了源碼級優化,包括暴露更具體的MemTable內存使用現狀,替換更高效的系統內存分配器等。

合理配置Compaction相關參數

Compaction相關參數較多,對於寫密集型業務,若配置不當會導致業務停寫問題(write stall)。在雲音樂實時推薦場景,我們通過合理調優Compaction參數來實現MyRocks讀寫性能平穩輸出。

網易雲背後的數據庫:Facebook開源,完全兼容MySQL


上圖右上側為未調優時的性能曲線,波動非常劇烈,最高1w+,最低僅2k+。這是由於寫入壓力過大頻繁觸發停寫問題。

網易雲背後的數據庫:Facebook開源,完全兼容MySQL

顯然,進行參數調優時,如果參數能夠在線調整,可以避免數據庫服務重啟導致業務受影響,所以我們通過代碼優化儘可能增加了在線可調的參數個數。

【推薦理由】

一、 MyRocks拓展了MySQL應用領域,使其可以在要求更高性能的業務場景上使用;
二、 MyRocks減少了MySQL所需的存儲空間,節省了業務的硬件投入成本;
三、 有大量MyRocks能夠發揮優勢的使用場景。


來源:網易工程師--溫正湖

有任何問題歡迎留言交流~

整理總結不易,如果覺得這篇文章有意思的話,歡迎轉發、收藏,給我一些鼓勵~

有想看的內容或者建議,敬請留言!

最近利用空餘時間整理了一些精選Java架構學習視頻和大廠項目底層知識點,需要的同學歡迎私信我發給你~一起學習進步!有任何問題也歡迎交流~

Java日記本,每日存檔超實用的技術乾貨學習筆記,每天陪你前進一點點~


分享到:


相關文章: