Facebook TSDB论文翻译
完成于 2017.03.21
本文為Facebook論文的翻譯,?約1.5萬字
原文地址?http://www.vldb.org/pvldb/vol8/p1816-teller.pdf
特別感謝阿里巴巴?葉翔、悠你、興博?的幫助
如發(fā)現(xiàn)內(nèi)容有誤請(qǐng)與我聯(lián)系?6623662005@163.com
更多的技術(shù)分享請(qǐng)關(guān)注公眾號(hào):mongodb_side
概要
大型互聯(lián)網(wǎng)服務(wù)一般以出現(xiàn)故障及時(shí)響應(yīng)和保持高可用性為目標(biāo)。為了提供正常穩(wěn)定的服務(wù),通常要每秒從大量系統(tǒng)中監(jiān)控和分析數(shù)以千萬計(jì)的數(shù)據(jù)(性能數(shù)據(jù)和業(yè)務(wù)數(shù)據(jù))。一個(gè)特別高效的解決方案是用TSDB對(duì)這些數(shù)據(jù)進(jìn)行存儲(chǔ)和查詢。
?
設(shè)計(jì)TSDB時(shí)的一個(gè)關(guān)鍵挑戰(zhàn)是如何在性能、擴(kuò)展性、穩(wěn)定性這幾者之間做出恰當(dāng)?shù)钠胶狻1酒撐闹饕榻BGorilla,Facebook的內(nèi)存級(jí)TSDB。核心的思想是對(duì)于監(jiān)控系統(tǒng)來說,用戶更關(guān)心數(shù)據(jù)的聚合分析,而不是具體某個(gè)時(shí)間點(diǎn)的數(shù)據(jù);在快速檢測(cè)和診斷一個(gè)正在發(fā)生的問題的根本原因時(shí),最近的數(shù)據(jù)會(huì)比舊的數(shù)據(jù)更有價(jià)值。Gorilla針對(duì)高可用的讀寫做了優(yōu)化,發(fā)生故障時(shí)甚至會(huì)以丟失少量最近寫入的數(shù)據(jù)為代價(jià)來保證整體的可用性。為了提升查詢性能,我們專門使用了一些壓縮技術(shù),例如delta-of-delta編碼的時(shí)間戳、浮點(diǎn)型值的XOR壓縮運(yùn)算,這些技術(shù)讓Gorilla減少了10倍左右的存儲(chǔ)空間。存儲(chǔ)空間的大幅減少使得我們可以將數(shù)據(jù)存儲(chǔ)在內(nèi)存中,與傳統(tǒng)的基于Hbase的TSDB相比,查詢耗時(shí)縮短了73倍,吞吐量提高了14倍。性能上的提升可以擴(kuò)展出更多監(jiān)控和排查問題的工具,例如時(shí)間序列關(guān)聯(lián)搜索,數(shù)據(jù)更加豐富和密集的可視化工具。Gorilla也非常優(yōu)雅的解決了單節(jié)點(diǎn)故障,讓整個(gè)集群沒有額外的運(yùn)維開銷。
?
1.???介紹
大型互聯(lián)網(wǎng)服務(wù)旨在保持高可用性和對(duì)用戶及時(shí)響應(yīng),即使是在出現(xiàn)意外故障的情況下。隨著業(yè)務(wù)的增長(zhǎng),要支持更為龐大的全球化業(yè)務(wù)就需要將“少量系統(tǒng)、數(shù)百機(jī)器”擴(kuò)展到上千個(gè)單獨(dú)的系統(tǒng),運(yùn)行在數(shù)千甚至更多的機(jī)器上,通常還要跨越多個(gè)不同地域的數(shù)據(jù)中心。
?
運(yùn)維這些大規(guī)模服務(wù)的一個(gè)重要前提是能夠非常精準(zhǔn)的監(jiān)控系統(tǒng)的運(yùn)行狀況和性能,當(dāng)問題出現(xiàn)時(shí)能夠快速定位和診斷。Facebook使用時(shí)間序列數(shù)據(jù)庫(kù)(TSDB)來存儲(chǔ)系統(tǒng)的各項(xiàng)指標(biāo)數(shù)據(jù),并提供快速查詢功能。后來在監(jiān)控和運(yùn)維Facebook服務(wù)時(shí)我們遇到了一些技術(shù)上的瓶頸,于是我們?cè)O(shè)計(jì)了Gorilla,一個(gè)內(nèi)存級(jí)的TSDB,它每秒能存儲(chǔ)千萬級(jí)的數(shù)據(jù)(例如 cpu load, error rate, latency等),并能毫秒級(jí)返回基于這些數(shù)據(jù)的查詢。
?
寫入
我們對(duì)TSDB最核心的要求是能夠保障數(shù)據(jù)寫入的高可用性。由于我們有幾百個(gè)系統(tǒng)、大量數(shù)據(jù)維度,數(shù)據(jù)的寫入量很容易就會(huì)達(dá)到每秒千萬級(jí)。相反,數(shù)據(jù)的讀取量通常少好幾個(gè)量級(jí),這是因?yàn)樽x取主要是由一些自動(dòng)化的監(jiān)控系統(tǒng)發(fā)起,這些系統(tǒng)只會(huì)關(guān)心相對(duì)重要的數(shù)據(jù),數(shù)據(jù)可視化系統(tǒng)也會(huì)占用一些讀取量,它們將數(shù)據(jù)加工后通過數(shù)據(jù)面板、圖表等方式呈現(xiàn)出來,另外,當(dāng)需要去定位解決一個(gè)線上問題時(shí),也會(huì)人為的發(fā)起一些數(shù)據(jù)讀取操作。
?
狀態(tài)轉(zhuǎn)換
我們希望當(dāng)系統(tǒng)的運(yùn)行狀況發(fā)生重大變化時(shí)能夠在第一時(shí)間發(fā)現(xiàn)問題,例如新版本發(fā)布、某個(gè)線上變更引發(fā)異常、網(wǎng)絡(luò)故障,或者其它一些原因。因此我們的TSDB需要具備在很短的時(shí)間內(nèi)細(xì)粒度聚合計(jì)算的能力。這種在幾十秒內(nèi)迅速檢測(cè)到系統(tǒng)狀態(tài)變化的能力是非常有價(jià)值的,基于它就可以在故障擴(kuò)散之前自動(dòng)的做快速修復(fù)。
?
高可用
如果因?yàn)榫W(wǎng)絡(luò)分區(qū)或者其它故障引發(fā)數(shù)據(jù)中心之間斷連,每個(gè)系統(tǒng)當(dāng)前連接的數(shù)據(jù)中心都應(yīng)該具備將數(shù)據(jù)寫入到本網(wǎng)絡(luò)域內(nèi)TSDB服務(wù)器的能力,并且在需要時(shí)可以在這些數(shù)據(jù)上做查詢檢索。
?
容錯(cuò)
我們希望寫操作能同時(shí)復(fù)制到多個(gè)節(jié)點(diǎn)上,當(dāng)某些數(shù)據(jù)中心或不同地理位置的節(jié)點(diǎn)發(fā)生不可預(yù)知的災(zāi)難時(shí),也能承受損失。
?
Gorilla是Facebook研發(fā)的新的TSDB,在設(shè)計(jì)上實(shí)現(xiàn)了上文描述的幾個(gè)點(diǎn)。Gorilla采用write-through cache方式將最近據(jù)記錄到監(jiān)控系統(tǒng)。我們的目標(biāo)是讓大多數(shù)查詢?cè)?0ms內(nèi)返回。
?
Gorilla的設(shè)計(jì)思路遵循一個(gè)觀點(diǎn),我們認(rèn)為人們?cè)谑褂帽O(jiān)控系統(tǒng)時(shí)不會(huì)關(guān)注于孤立的數(shù)據(jù)點(diǎn),而是更在意整體的聚合分析。此外,這些系統(tǒng)不會(huì)存儲(chǔ)任何用戶數(shù)據(jù),所以傳統(tǒng)的ACID特性并非TSDB的核心需求。但是,Gorilla在任何時(shí)候都要保障大部分的寫都能成功,即使是面對(duì)那些可能使整個(gè)數(shù)據(jù)中心都無法訪問的重大故障時(shí)也要如此。還有一點(diǎn),最近的數(shù)據(jù)比舊的數(shù)據(jù)更有價(jià)值,從運(yùn)維工程師的角度來說,排查某個(gè)系統(tǒng)或服務(wù)現(xiàn)在正在發(fā)生故障比排查一個(gè)小時(shí)前出的故障更容易給出直覺上的判斷。Gorilla對(duì)高可用的讀寫做了優(yōu)化,即使在發(fā)生故障時(shí),也會(huì)以丟失少量數(shù)據(jù)為代價(jià)來保證整體的可用性。
?
技術(shù)上的挑戰(zhàn)來自于高性能寫入、承載的數(shù)據(jù)總量、實(shí)時(shí)聚合能力,以及穩(wěn)定性方面的需求。我們依次解決了這些問題。為了實(shí)現(xiàn)前兩個(gè)需求,我們分析了在Facebook使用很廣并且也用了很久的監(jiān)控系統(tǒng)– ODS時(shí)間序列數(shù)據(jù)庫(kù)。我們注意到ODS中至少有85%的查詢來自過去26小時(shí)采集的數(shù)據(jù)。進(jìn)一步分析后我們決定將基于磁盤的數(shù)據(jù)庫(kù)替換為基于內(nèi)存的數(shù)據(jù)庫(kù),這樣就能提供最好的服務(wù)。另一方面,將內(nèi)存數(shù)據(jù)庫(kù)做為磁盤存儲(chǔ)的緩存,可以實(shí)現(xiàn)內(nèi)存級(jí)的寫入速度并擁有基于磁盤的數(shù)據(jù)持久性。
?
截止到2015年春天,Facebook的監(jiān)控系統(tǒng)產(chǎn)生了超過20億個(gè)唯一的時(shí)間序列計(jì)數(shù)器,每秒增加約1200萬個(gè)數(shù)據(jù)點(diǎn),這意味著每天會(huì)增加1萬億個(gè)數(shù)據(jù)點(diǎn)。每個(gè)數(shù)據(jù)點(diǎn)16個(gè)字節(jié),每天就需要16TB的內(nèi)存空間,這對(duì)于實(shí)際的部署而言是巨大的資源消耗。我們通過重用現(xiàn)有的基于XOR的浮點(diǎn)型數(shù)據(jù)壓縮算法以流的方式解決了這個(gè)問題,平均每個(gè)點(diǎn)的壓縮到1.37字節(jié),縮小了12倍。
?
我們?cè)诙鄠€(gè)不同地域的數(shù)據(jù)中心部署了Gorilla實(shí)例,并在實(shí)例之間做數(shù)據(jù)傳輸,但不會(huì)試圖去保證數(shù)據(jù)的一致性,基于這種方式實(shí)現(xiàn)了穩(wěn)定性方面的需求。數(shù)據(jù)讀取會(huì)路由到最近且可用的Gorilla實(shí)例上。請(qǐng)注意,這種設(shè)計(jì)方式是基于我們對(duì)實(shí)際業(yè)務(wù)的理解,因?yàn)槲覀冋J(rèn)為個(gè)別數(shù)據(jù)的丟失對(duì)整體的數(shù)據(jù)聚合不會(huì)有太大的影響,除非兩個(gè)Gorilla實(shí)例之間存在重大的數(shù)據(jù)不一致。
?
Gorilla目前部署在Facebook的生產(chǎn)環(huán)境中,工程師們把它當(dāng)做日常的實(shí)時(shí)數(shù)據(jù)工具,并協(xié)同其它監(jiān)控和分析系統(tǒng)(例如Hive、Scuba)一起檢測(cè)和診斷問題。
2.???背景和需求
2.1 ODS
Facebook的基礎(chǔ)設(shè)施由數(shù)以百計(jì)個(gè)分布在不同數(shù)據(jù)中心的系統(tǒng)組成,如果沒有一個(gè)監(jiān)控系統(tǒng)來跟蹤它們的健康和性能,要操作和管理它們是非常困難的。“操作數(shù)據(jù)存儲(chǔ)”(ODS)是Facebook監(jiān)控系統(tǒng)的一個(gè)重要部分。ODS包括一個(gè)時(shí)間序列數(shù)據(jù)庫(kù)(TSDB),一個(gè)查詢服務(wù),以及一個(gè)檢測(cè)和報(bào)警系統(tǒng)。ODS的TSDB是在HBase存儲(chǔ)系統(tǒng)之上建立的,相關(guān)描述見文末引用[26]。圖1整體展示了ODS的協(xié)作方式,時(shí)間序列數(shù)據(jù)由Facebook主機(jī)上的服務(wù)產(chǎn)生,通過ODS的寫入服務(wù)收集,最終寫入到HBase中。
ODS的時(shí)間序列數(shù)據(jù)有兩類使用者。第一類使用者是那些依賴報(bào)表系統(tǒng)做數(shù)據(jù)分析的工程師,系統(tǒng)提供的圖表以及其它可視化的交互分析都要從ODS中獲取數(shù)據(jù);第二類使用者是我們的自動(dòng)化報(bào)警系統(tǒng),它從ODS中讀取計(jì)數(shù)器,將其與系統(tǒng)健康、性能、診斷指標(biāo)等預(yù)設(shè)閾值進(jìn)行比較,在必要時(shí)給oncall的工程師們和自動(dòng)修復(fù)系統(tǒng)發(fā)出警報(bào)。
?
2.1.1?監(jiān)控系統(tǒng)讀取數(shù)據(jù)時(shí)的性能問題
2013年初,Facebook的監(jiān)控團(tuán)隊(duì)逐漸意識(shí)到HBase時(shí)間序列存儲(chǔ)系統(tǒng)難以擴(kuò)展支撐未來的讀取負(fù)載。在展現(xiàn)各種用于分析的圖表時(shí),平均的查詢耗時(shí)是可以接受的,但是對(duì)于自動(dòng)化系統(tǒng)來說,由于會(huì)產(chǎn)生大量的查詢操作,排在末段的查詢需要等前面的操作完成,時(shí)間的疊加導(dǎo)致它們可能需要等待數(shù)秒才會(huì)被執(zhí)行到,系統(tǒng)就會(huì)被阻塞住。另外,如果對(duì)幾千個(gè)時(shí)間序列進(jìn)行中頻次查詢要花掉幾十秒的時(shí)間,人們也會(huì)對(duì)自己使用方式產(chǎn)生懷疑;對(duì)稀疏數(shù)據(jù)集做并發(fā)更高的查詢可能會(huì)超時(shí),這是因?yàn)镠Base的數(shù)據(jù)存儲(chǔ)已經(jīng)將策略調(diào)整為寫入優(yōu)先。雖然基于HBase的TSDB比較低效,但是我們也不能立刻就全部換掉,因?yàn)镺DS上的HBase存儲(chǔ)占用了2PB的數(shù)據(jù)。Facebook的數(shù)據(jù)倉(cāng)庫(kù)解決方案Hive也存在問題,它比ODS的查詢延時(shí)還要高幾個(gè)數(shù)量級(jí),而查詢的延時(shí)和效率恰恰是我們最關(guān)心的。
?
接下來我們將注意力放在了內(nèi)存級(jí)的緩存上。ODS先前使用了一個(gè)簡(jiǎn)單的read-through cache讀取方式,但它主要針對(duì)的是圖表系統(tǒng),它里面的多個(gè)顯示面板會(huì)共享相同的時(shí)間序列數(shù)據(jù)。一個(gè)特別麻煩的問題是,當(dāng)需要查詢最近的數(shù)據(jù)時(shí),如果緩存miss了就會(huì)直接將請(qǐng)求打到HBase存儲(chǔ)上。我們也考慮了單獨(dú)基于memcache的write-through緩存方案,最終還是沒有采納,這是因?yàn)閷⑿聰?shù)據(jù)添加到已有的時(shí)間序列上時(shí)需要先讀再寫,這會(huì)對(duì)memcache服務(wù)器產(chǎn)生極大的壓力。我們需要一個(gè)更有效的解決方案。
?
2.2 Gorilla的需求
基于多方面的考慮,我們確定了下列需求:
-
支撐20億個(gè)通過字符串key唯一標(biāo)識(shí)的時(shí)間序列
-
每分鐘能增加7億個(gè)數(shù)據(jù)點(diǎn)(時(shí)間戳和值)
-
內(nèi)存能存儲(chǔ)26個(gè)小時(shí)的數(shù)據(jù)
-
高峰時(shí)每秒能支撐40000次以上的查詢
-
1毫秒內(nèi)能讀取成功
-
時(shí)間序列支持15秒的間隔粒度(即每分鐘每個(gè)時(shí)序序列有4個(gè)點(diǎn))
-
數(shù)據(jù)存儲(chǔ)在2個(gè)不同的副本中(容災(zāi)能力)
-
即使某個(gè)服務(wù)器掛了也能持續(xù)提供讀取
-
能快速掃描內(nèi)存中的所有數(shù)據(jù)
-
每年能支撐最少兩倍的業(yè)務(wù)增長(zhǎng)
本文的第3節(jié),簡(jiǎn)單比較了其它幾個(gè)TSDB,在第4節(jié)我們?cè)敿?xì)介紹Gorilla的實(shí)現(xiàn),4.1首次講述新的時(shí)間戳和數(shù)據(jù)值的壓縮方法,4.4介紹當(dāng)出現(xiàn)區(qū)域性的故障時(shí)Gorilla怎樣保障高可用。第5節(jié)介紹圍繞Gorilla打造的新工具。論文會(huì)以第6節(jié)介紹我們?cè)陂_發(fā)和部署Gorilla方面的經(jīng)驗(yàn)來結(jié)束。
?
3.???和其它TSDB比較
有很多已出版的書刊或論文里詳細(xì)介紹了通過數(shù)據(jù)挖掘技術(shù)來高效的搜索、分類、聚合大量時(shí)間序列數(shù)據(jù)。它們系統(tǒng)的描述了時(shí)間序列數(shù)據(jù)的很多種使用方式,從數(shù)據(jù)收集到分類,再到異常檢測(cè),再到索引時(shí)間序列。然而詳細(xì)描述系統(tǒng)如何實(shí)時(shí)收集和存儲(chǔ)大量時(shí)間序列的示例比較少。Gorilla的設(shè)計(jì)專注于打造生產(chǎn)環(huán)境下可靠的實(shí)時(shí)監(jiān)控,從其它TSDB的比較中脫穎而出。Gorilla有一個(gè)比較獨(dú)特的設(shè)計(jì)思想,面對(duì)故障時(shí)保障讀寫的高可用比保障老數(shù)據(jù)的可用性具有更高的優(yōu)先級(jí)。
由于Gorilla從一開始就被設(shè)計(jì)成將所有數(shù)據(jù)都存儲(chǔ)在內(nèi)存中,因此在內(nèi)存結(jié)構(gòu)上也不同于現(xiàn)有的TSDB。如果將Gorilla看作一個(gè)中間存儲(chǔ),用來在基于磁盤的TSDB上層的內(nèi)存中存儲(chǔ)時(shí)間序列數(shù)據(jù),那么Gorilla可以以一個(gè)write-through cache方式用在任意的TSDB上(通過相對(duì)簡(jiǎn)單的修改)。Gorilla在數(shù)據(jù)寫入速度和水平擴(kuò)展能力上與已有方案類似。
?
3.1 OpenTSDB
OpenTSDB基于HBase,和ODS中用來存儲(chǔ)long term數(shù)據(jù)的HBase存儲(chǔ)層非常相似。兩個(gè)系統(tǒng)都依賴相似的表結(jié)構(gòu),在優(yōu)化和水平擴(kuò)展上也有比較近似的方案結(jié)論。但是,我們發(fā)現(xiàn)在支撐構(gòu)建更高級(jí)監(jiān)控工具的高查詢量時(shí),要比基于磁盤的存儲(chǔ)所能提供的查詢更快。
和OpenTSDB不同,ODS HBase存儲(chǔ)層會(huì)定時(shí)的將老數(shù)據(jù)進(jìn)行聚合以節(jié)省空間,這導(dǎo)致ODS中的老數(shù)據(jù)相比更近的數(shù)據(jù)時(shí)間間隔粒度更大,而OpenTSDB永久保存全量數(shù)據(jù)。我們發(fā)現(xiàn)從成本較低的長(zhǎng)時(shí)間片查詢以及空間的節(jié)省上來說,數(shù)據(jù)精度的丟失是可以接受的。
OpenTSDB還有一個(gè)更豐富的數(shù)據(jù)模型來唯一識(shí)別時(shí)間序列,每個(gè)時(shí)間通過一組任意的k/v對(duì)來標(biāo)識(shí),也稱為tags。Gorilla使用單個(gè)字符串key來標(biāo)識(shí)時(shí)間序列,并依賴更高級(jí)的工具來提取和識(shí)別時(shí)間序列元數(shù)據(jù)。
?
3.2 Whisper(Graphite)
Graphite是一個(gè)RRD數(shù)據(jù)庫(kù),它用Whisper內(nèi)置的格式將時(shí)間序列數(shù)據(jù)存儲(chǔ)在本地磁盤上,這個(gè)格式假設(shè)時(shí)間序列數(shù)據(jù)是按固定時(shí)間間隔產(chǎn)生的,不支持間隔跳動(dòng)時(shí)間序列。Gorilla在對(duì)固定時(shí)間間隔的數(shù)據(jù)處理上效率更高,并且能支持任意和不斷變化的時(shí)間間隔。Whisper中的每個(gè)時(shí)間序列都存儲(chǔ)在單獨(dú)的文件中,一定時(shí)間之后新的數(shù)據(jù)會(huì)覆蓋老的數(shù)據(jù)。Gorilla使用了類似的方式,只不過最近的數(shù)據(jù)是存儲(chǔ)在內(nèi)存中。但是,由于Graphite/Whisper采用的是磁盤存儲(chǔ),對(duì)于Gorilla要解決的問題來說,查詢耗時(shí)還是太高了。
?
3.3 InfluxDB
InfluxDB是一個(gè)新的開源時(shí)間序列數(shù)據(jù)庫(kù),和OpenTSDB相比有更豐富的數(shù)據(jù)模型,時(shí)間序列中的每一個(gè)事件都可以包含完整的元數(shù)據(jù),盡管這樣更具靈活性,但是和只在數(shù)據(jù)庫(kù)中保存時(shí)間序列相比,必然導(dǎo)致更大的磁盤占用率。
InfluxDB還包含一些可以擴(kuò)展的代碼,允許用戶的這些代碼上將它水平擴(kuò)展為分布式存儲(chǔ)集群,而不需要像OpenTSDB那樣還有運(yùn)維HBase/Hadoop集群的開銷。在Facebook,我們已經(jīng)有專門的團(tuán)隊(duì)來支持HBase設(shè)施,將他們用在ODS不需要投入大量額外的資源。和其它系統(tǒng)一樣,InfluxDB將數(shù)據(jù)保存在磁盤上,這也導(dǎo)致查詢速度比存儲(chǔ)在內(nèi)存中慢。
?
4.???Gorilla架構(gòu)
Gorilla是一個(gè)基于內(nèi)存的TSDB,在監(jiān)控?cái)?shù)據(jù)寫入HBase存儲(chǔ)時(shí),起到一個(gè)write-through cache的作用。存儲(chǔ)在Gorilla的監(jiān)控?cái)?shù)據(jù)是一個(gè)簡(jiǎn)單的3元組字符串key,時(shí)間戳是64位整型,值是雙精度浮點(diǎn)型。Gorilla采用了一種新的時(shí)間序列壓縮算法,可以按照時(shí)間序?qū)?shù)據(jù)從16字節(jié)壓縮到平均1.37字節(jié),縮小12倍。此外,我們專門設(shè)計(jì)了Gorilla的內(nèi)存數(shù)據(jù)結(jié)構(gòu),在保持對(duì)單個(gè)時(shí)間序列進(jìn)行時(shí)間段查找的同時(shí)也能快速和高效的進(jìn)行全數(shù)據(jù)掃描。
監(jiān)控?cái)?shù)據(jù)中定義的key用來唯一標(biāo)識(shí)一個(gè)時(shí)間序列,通過對(duì)基于key的數(shù)據(jù)進(jìn)行分片,每個(gè)時(shí)間序列數(shù)據(jù)集都會(huì)被映射到一臺(tái)單獨(dú)的Gorilla主機(jī)上。因此,我們可以通過簡(jiǎn)單的擴(kuò)展主機(jī)并調(diào)整分片算法將新的時(shí)間序列數(shù)據(jù)映射到新的主機(jī)上,從而達(dá)到擴(kuò)展Gorilla的目的。Gorilla 18個(gè)月前在生產(chǎn)環(huán)境運(yùn)行時(shí),26小時(shí)內(nèi)的全量時(shí)間序列數(shù)據(jù)占用1.3TB的內(nèi)存,均勻分布在20臺(tái)機(jī)器上。在那之后,我們必須將集群的規(guī)模擴(kuò)為兩倍來應(yīng)對(duì)兩倍的數(shù)據(jù)增長(zhǎng),現(xiàn)在每個(gè)Gorilla集群有80臺(tái)機(jī)器在運(yùn)行。擴(kuò)展的過程很簡(jiǎn)單,這是因?yàn)闊o狀態(tài)的架構(gòu)有非常好的水平擴(kuò)展能力。
Gorilla將時(shí)間序列數(shù)據(jù)寫到不同地域的主機(jī)中,這樣就能容忍單節(jié)點(diǎn)故障,網(wǎng)絡(luò)切換,甚至是整個(gè)數(shù)據(jù)中心故障。在檢測(cè)到故障時(shí),所有讀取操作會(huì)failed over到備用節(jié)點(diǎn),以確保用戶不會(huì)感知到任何中斷。
?
4.1?時(shí)間序列壓縮
在評(píng)估創(chuàng)建新內(nèi)存級(jí)時(shí)間序列數(shù)據(jù)庫(kù)的可行性時(shí),我們考慮了幾種現(xiàn)有的壓縮方案,以減少存儲(chǔ)上的開銷。我們認(rèn)為僅適用于整型數(shù)據(jù)的壓縮技術(shù)不能滿足存儲(chǔ)雙精度浮點(diǎn)型數(shù)據(jù)的需求;其它的一些技術(shù)作用于完整的數(shù)據(jù)集,但不支持對(duì)存儲(chǔ)在Gorilla中的數(shù)據(jù)流進(jìn)行壓縮;我們還發(fā)現(xiàn)數(shù)據(jù)挖掘領(lǐng)域會(huì)使用有損的時(shí)間序列近似技術(shù),這樣會(huì)更適合用內(nèi)存來存儲(chǔ),但是Gorilla更關(guān)注于保持?jǐn)?shù)據(jù)的完整性。
我們受到了從科學(xué)計(jì)算中推導(dǎo)出來的浮點(diǎn)型壓縮方法的啟發(fā),該方法利用與前面值的XOR比較來生成一個(gè)差值編碼。
Gorilla對(duì)時(shí)間序列中的數(shù)據(jù)點(diǎn)進(jìn)行壓縮,不會(huì)有額外的跨時(shí)間序列壓縮。每個(gè)數(shù)據(jù)點(diǎn)是一對(duì)64位的值,代表那個(gè)時(shí)間的時(shí)間戳和值。時(shí)間戳和值根據(jù)前面值分別進(jìn)行壓縮。整體的壓縮方案見圖2,圖里展示了時(shí)間戳和值是如何在壓縮塊中運(yùn)算的。
圖2-a表明時(shí)間序列數(shù)據(jù)是由時(shí)間戳和值組成的數(shù)據(jù)流,Gorilla按照時(shí)間分區(qū)將數(shù)據(jù)流壓縮到數(shù)據(jù)塊中。這里先定義了一個(gè)由基線時(shí)間構(gòu)建的Header(圖例中從2點(diǎn)開始),然后將第一個(gè)值進(jìn)行了簡(jiǎn)單的壓縮存儲(chǔ),圖2-b是通過delta-of-delta壓縮后的時(shí)間戳,這個(gè)在4.1.1節(jié)會(huì)做更詳細(xì)的描述。圖中給出的delta of delta值為-2,用2位來存儲(chǔ)header(‘10’),7位來存儲(chǔ)值,總位數(shù)只有9位。圖2-c顯示了XOR壓縮后的浮點(diǎn)值,4.1.2節(jié)有更詳細(xì)的描述。通過將浮點(diǎn)值與前面的值進(jìn)行XOR操作,我們發(fā)現(xiàn)只有一個(gè)有意義的位。用兩位編碼header(‘11’),編碼中有11個(gè)前置0,一個(gè)有意義的位,其實(shí)際值為(‘1’),一共用14位進(jìn)行存儲(chǔ)。
?
4.1.1?時(shí)間戳壓縮
我們分析了ODS中存儲(chǔ)的時(shí)間序列數(shù)據(jù),決定對(duì)Gorilla的壓縮方案做優(yōu)化。我們發(fā)現(xiàn)絕大部分ODS數(shù)據(jù)在固定的時(shí)間間隔產(chǎn)生,例如每60秒記錄一條數(shù)據(jù)的時(shí)間序列普遍存在,偶爾有一些數(shù)據(jù)有提前或推遲1秒生產(chǎn)出來,但在入口一般都是有約束的。
相比于存儲(chǔ)整個(gè)時(shí)間戳,我們只存儲(chǔ)的“差值的差值”,這樣會(huì)更高效。如果某個(gè)時(shí)間序列后續(xù)的時(shí)間與前面時(shí)間的差值分別為60,60,59,61,那么“差值的差值”是用當(dāng)前的時(shí)間戳差值前去前一個(gè)差值,那么計(jì)算出的“差值的差值”為0,-1,2。圖2給出了示例。
接下來我們用下面的規(guī)則對(duì)“差值的差值”做可變長(zhǎng)的編碼:
1. 數(shù)據(jù)塊的header存儲(chǔ)了一個(gè)開始的時(shí)間戳t-1,這里對(duì)齊到2點(diǎn)鐘;第一個(gè)時(shí)間戳t0,用14位存儲(chǔ)與t-1的差值
2. 對(duì)于時(shí)間戳tn:
????a) 計(jì)算差值的差值為:D = (tn –tn-1) –(tn-1 –tn-2)
????b) 如果D=0,用一個(gè)單獨(dú)的位來存儲(chǔ)’0’
????c) 如果D在[-63,64]之間,存’10’,后面為值(7位)
????d) 如果D在[-255,256]之間,存’110’,后面為值(9位)
????e) 如果D在[-2047,2048]之間,存’1110’,后面為值(12位)
????f)其它情況存’1111’,后面用32位存D的值
?
這些不同取值范圍是從生產(chǎn)環(huán)境真實(shí)的時(shí)間序列中采樣出來的,每個(gè)值都能選擇合適的范圍以達(dá)到最好的壓縮比。雖然一個(gè)時(shí)間序列可能有時(shí)會(huì)丟失部分?jǐn)?shù)據(jù),但是它現(xiàn)存的數(shù)據(jù)很可能都是以固定的時(shí)間間隔產(chǎn)生的。舉個(gè)例子,假設(shè)在丟失了一個(gè)數(shù)據(jù)點(diǎn)后的差值為60,60,121,59,那么差值的差值就是0,61,-62。61和-62適配最小的取值范圍,就會(huì)更少的位數(shù)來編碼。下一個(gè)取值范圍[-255, 256]也很有用,當(dāng)每4分鐘產(chǎn)生一條數(shù)據(jù)時(shí),如果丟失了某條數(shù)據(jù)仍然可以適配這個(gè)取值范圍。
圖3展示了Gorilla中時(shí)間戳最終值的分布情況,我們發(fā)現(xiàn)有96%的時(shí)間戳都能被壓縮到1個(gè)單獨(dú)的位來存儲(chǔ)。
?
4.1.2?值壓縮
除了對(duì)時(shí)間戳做壓縮外,Gorilla也對(duì)值進(jìn)行了壓縮,3元組字符串中的數(shù)據(jù)值為雙精度浮點(diǎn)型。我們使用的壓縮方案和現(xiàn)在已有的浮點(diǎn)型數(shù)據(jù)壓縮算法類似,文末的參考文獻(xiàn)[17]和[25]有相關(guān)描述。
通過分析ODS的數(shù)據(jù)發(fā)現(xiàn),大多數(shù)時(shí)間序列內(nèi)相鄰數(shù)據(jù)點(diǎn)的值不會(huì)有明顯的變化,此外,很多數(shù)據(jù)來源只會(huì)存儲(chǔ)整型的值。這就使得我們可以將文末參考文獻(xiàn)[25]的昂貴方案調(diào)整為更簡(jiǎn)單的實(shí)現(xiàn),僅用將當(dāng)前值和前面的值做比較。如果值接近,那么浮點(diǎn)型數(shù)據(jù)的符號(hào)位,指數(shù)位和尾數(shù)部分的前幾位,會(huì)是完全相同的,基于這點(diǎn),我們對(duì)當(dāng)前值和前序值使用一個(gè)簡(jiǎn)單的XOR運(yùn)算,而不是像時(shí)間戳那樣用差值編碼的方案。
?
我們用下面的規(guī)則對(duì)XOR后的值進(jìn)行可變長(zhǎng)編碼:
1. 第一個(gè)值不做壓縮
2. 如果與前序值的XOR結(jié)果為0(即值相同),僅用一位存儲(chǔ),值為’0’
3. 如果XOR結(jié)果非0,控制位的第一位存’1’,接下來的值為下面兩種之一
????a) 控制位’0’ — 有意義的位(即中間非0部分)的數(shù)據(jù)塊被前一個(gè)數(shù)據(jù)塊包含,例如,與前序值相比至少有同樣多的前置0和同樣多的尾部0,那么就可以直接的數(shù)據(jù)塊中使用這些信息,并且僅需要存儲(chǔ)非0的XOR值。
????b) 控制位’1’ — 用接下來的5位來存儲(chǔ)前置0的數(shù)量,然后用6位存儲(chǔ)XOR中間非0位的長(zhǎng)度,最后再存儲(chǔ)中間非0位
?
使用XOR運(yùn)算編碼對(duì)時(shí)間序列高效的存儲(chǔ)壓縮方案在圖2有直觀的展現(xiàn)。
圖5是Gorilla中實(shí)際的數(shù)據(jù)分布情況,大約有59%值只用了1位存儲(chǔ),也就是當(dāng)前值和前序值完全一樣;28.3%控制位為’10’(上面提到的規(guī)則a),平均占用26.6位;余下12.6%的控制位為’11’,平均占用36.9位,位數(shù)多是因?yàn)閷?duì)前置0和中間非0位的長(zhǎng)度編碼需要額外13位。
?
這種壓縮算法同時(shí)使用了前序值和前序XOR值,這樣會(huì)使最終的結(jié)果值具有更好的壓縮率,這是因?yàn)橐欢芜B續(xù)XOR值的前置0和尾部0個(gè)數(shù)往往非常接近,見圖4。這種算法對(duì)整型的壓縮效果更好,整型值經(jīng)過XOR運(yùn)算后的中間段位的位置一般在整個(gè)時(shí)間序列中對(duì)齊的,意味著大多數(shù)XOR值有相同個(gè)數(shù)的尾部0。
?
我們的編碼方案有一個(gè)折衷是壓縮算法的時(shí)間跨度,在更長(zhǎng)的時(shí)間跨度上使用同樣的編碼能夠獲得更好的壓縮比,但是這個(gè)跨度上的短時(shí)間區(qū)間查詢可能需要在數(shù)據(jù)解碼上消耗額外的計(jì)算資源。圖6是存儲(chǔ)在ODS中的時(shí)間序列在不同數(shù)據(jù)塊大小下的平均壓縮率,可以看出塊大小超過兩個(gè)小時(shí)以上后,數(shù)據(jù)的壓縮率收益是逐漸減少的,一個(gè)兩小時(shí)時(shí)長(zhǎng)的塊可以將每個(gè)點(diǎn)的數(shù)據(jù)壓縮到1.37字節(jié)。
?
4.2?內(nèi)存數(shù)據(jù)結(jié)構(gòu)
Gorilla實(shí)現(xiàn)中主要的數(shù)據(jù)結(jié)構(gòu)是一個(gè)時(shí)間序列Map (TSmap),圖7提供了這個(gè)數(shù)據(jù)結(jié)構(gòu)的整體概覽。TSmap包含一個(gè)C++標(biāo)準(zhǔn)庫(kù)中的vector,里面是指向時(shí)間序列的指針;還包含一個(gè)map,key為時(shí)間序列的名稱,不區(qū)分大小寫并保留原有大小寫,值是和vector中一樣的指針。vector可以實(shí)現(xiàn)全數(shù)據(jù)分頁(yè)查詢,而map可以支撐指定時(shí)間序列的定長(zhǎng)時(shí)間段查詢,要滿足快速查詢的需求必須要具備定長(zhǎng)時(shí)間段查詢的能力,同時(shí)也要滿足有效的數(shù)據(jù)掃描。
?
C++的指針可以在掃描數(shù)據(jù)時(shí)僅用幾微秒就能將整個(gè)vector或其中的幾頁(yè)拷貝,避免對(duì)新寫入到數(shù)據(jù)流的數(shù)據(jù)產(chǎn)生影響。被刪掉的時(shí)間序列在vector中為“墓碑狀態(tài)”,它的索引會(huì)被放置到一個(gè)空間的池中,當(dāng)產(chǎn)生新的時(shí)間序列時(shí)會(huì)復(fù)用它。“墓碑狀態(tài)”實(shí)際上是將一段內(nèi)存標(biāo)記為’dead’,并準(zhǔn)備好被重用,而不會(huì)實(shí)際將資源釋放到底層系統(tǒng)。
在TSmap上有一個(gè)讀-寫自旋鎖來保護(hù)對(duì)map和vector的訪問,每個(gè)時(shí)間序列上也有一個(gè)1字節(jié)的自旋鎖,通兩個(gè)鎖保證了并發(fā)的能力。對(duì)于每個(gè)單獨(dú)的時(shí)間序列來說寫的量相對(duì)校少,所以讀和寫也只有非常少的鎖爭(zhēng)用。
?
如圖7所示,分片唯一標(biāo)識(shí)(shardId)與TSmap之間的映射存在ShardMap中,它也是一個(gè)vector,存儲(chǔ)了TSmaps的指針,它使用了和TSmap一樣對(duì)大小寫不敏感的hash算法將時(shí)間序列名稱映射到各個(gè)分片,hash后的值在 [0,shard數(shù)量)區(qū)間內(nèi)。由于系統(tǒng)中分片的數(shù)量是恒定的,并且總量在幾千以內(nèi),所以存儲(chǔ)空指針的額外開銷基本上可以忽略。和TSmaps一樣,ShardMap有一個(gè)自旋鎖來處理并發(fā)訪問。
由于數(shù)據(jù)已經(jīng)劃分為分片,單個(gè)map可以保持足夠小(約100條記錄),C++標(biāo)準(zhǔn)庫(kù)中的unordered-map有足夠好的性能,沒有鎖爭(zhēng)用的問題。
?
時(shí)間序列的數(shù)據(jù)結(jié)構(gòu)有兩個(gè)重要組成部分,一部分是一系列關(guān)閉的數(shù)據(jù)塊,塊中的數(shù)據(jù)超過2小時(shí);另一部分是一個(gè)開放的數(shù)據(jù)塊,存最近的數(shù)據(jù)。開放塊是個(gè)append-only字符串,新的時(shí)間戳和值壓縮后追加這個(gè)字符串上。每個(gè)塊只存儲(chǔ)2小時(shí)的壓縮數(shù)據(jù),當(dāng)數(shù)據(jù)寫滿后塊會(huì)變?yōu)殛P(guān)閉,一旦塊關(guān)閉了就不能再對(duì)其做修改,除非將它從內(nèi)存中剔除。關(guān)閉后,會(huì)根據(jù)使用的slab總大小分配出一個(gè)新的塊來存數(shù)據(jù),這是因?yàn)槊看伍_放塊在關(guān)閉時(shí)實(shí)際用掉的空間都不一樣,我們發(fā)現(xiàn)使用這種方式在整體上會(huì)減少Gorilla產(chǎn)生的內(nèi)存碎片。
時(shí)間范圍查詢關(guān)聯(lián)的數(shù)據(jù)塊被會(huì)拷貝出來直接讀到調(diào)用端,返回給客戶端的是整個(gè)數(shù)據(jù)塊,使得解壓過程在Gorilla外完成。
?
4.3?磁盤數(shù)據(jù)結(jié)構(gòu)
Gorilla的目標(biāo)之一是能應(yīng)對(duì)單機(jī)故障。Gorilla通過將數(shù)據(jù)存儲(chǔ)在GlusterFS來實(shí)現(xiàn)持久化,GlusterFS是一個(gè)分布式文件系統(tǒng),三復(fù)本存儲(chǔ),兼容POSIX。HDFS或者其它分布式文件系統(tǒng)也同樣很容易應(yīng)對(duì)單機(jī)故障,我們同時(shí)也考慮了單主機(jī)數(shù)據(jù)庫(kù)比如MySQL和RocksDB,不過還是決定不使用這類數(shù)據(jù)庫(kù),因?yàn)槲覀兊某志没褂脠?chǎng)景不需要數(shù)據(jù)庫(kù)層面的查詢語言。
一臺(tái)Gorilla主機(jī)能存儲(chǔ)多個(gè)數(shù)據(jù)分片,每個(gè)分片上維護(hù)著一個(gè)文件目錄,每個(gè)文件目錄包括4種類型的文件:key列表,append-only日志,完整的塊文件,checkponit文件。
?
Key列表中的值是時(shí)間序列名和一個(gè)整型標(biāo)識(shí)的簡(jiǎn)單映射,整型標(biāo)識(shí)就是內(nèi)存中vector的下標(biāo)。新的key追加在這個(gè)列表中,Gorilla會(huì)定期對(duì)每個(gè)分片上的key做掃描,以便重寫到文件。
當(dāng)數(shù)據(jù)流入到Gorilla時(shí)也會(huì)被存儲(chǔ)到一個(gè)日志文件中,時(shí)間戳和值用前面4.1節(jié)描述的格式壓縮。但是每個(gè)分片上只有唯一的一個(gè)append-only日志文件,因此數(shù)據(jù)會(huì)交叉跨越多個(gè)時(shí)間序列。和內(nèi)存編碼不同的是,每個(gè)時(shí)間戳和值還要加上32位的整型ID做標(biāo)記,所以相比之下每個(gè)分片上的日志文件會(huì)增加明顯的存儲(chǔ)開銷。
Gorilla不提供ACID特性,同樣,上面提到的日志文件也不是WAL日志,數(shù)據(jù)被刷到磁盤之前會(huì)先到緩存區(qū),最多到64K,一般會(huì)包含1到2秒的數(shù)據(jù)。雖然在正常退出系統(tǒng)時(shí)緩沖區(qū)的數(shù)據(jù)會(huì)刷到磁盤,但是當(dāng)發(fā)生異常崩潰時(shí)可能會(huì)導(dǎo)致少部分?jǐn)?shù)據(jù)丟失。相比傳統(tǒng)的WAL日志帶來的收益,我們覺得這個(gè)取舍是值得的,因?yàn)榭梢砸愿焖俾蕦?shù)據(jù)刷到磁盤,也能支撐更加高可用的數(shù)據(jù)寫入。
每隔兩小時(shí), Gorilla將數(shù)據(jù)塊中的壓縮數(shù)據(jù)拷貝到磁盤,這種格式的數(shù)據(jù)遠(yuǎn)小于日志文件中的數(shù)據(jù)。每?jī)尚r(shí)的數(shù)據(jù)有一個(gè)完整的數(shù)據(jù)塊文件,它有兩部分:一組連續(xù)的64KB數(shù)據(jù)塊,它們直接從內(nèi)存中復(fù)制而來,以及一系列由<時(shí)間序列ID,數(shù)據(jù)塊指針>組成的值對(duì)。一旦某個(gè)塊文件完全刷到磁盤,Gorilla會(huì)刷下checkpoint文件并將相應(yīng)的日志刪除,checkpoint文件用來標(biāo)記一個(gè)完整的數(shù)據(jù)塊什么時(shí)候被刷到磁盤。如果在遇到進(jìn)程崩潰時(shí)塊文件沒有被成功刷到磁盤,那么在新的進(jìn)程啟動(dòng)時(shí)對(duì)應(yīng)的checkpoint文件是不存在的,因此這個(gè)時(shí)候每次啟動(dòng)新的進(jìn)程時(shí)除了讀取塊文件之外,還會(huì)從日志文件中讀取checkpoint之后的數(shù)據(jù)。
?
4.4?故障處理
對(duì)于容錯(cuò),我們選擇優(yōu)先考慮單節(jié)點(diǎn)故障,大規(guī)模的感知不到當(dāng)機(jī)時(shí)間的臨時(shí)性故障,以及區(qū)域性故障(比如整個(gè)區(qū)域網(wǎng)絡(luò)中斷)。這是因?yàn)閱喂?jié)點(diǎn)故障發(fā)生比較頻繁,而大規(guī)模的,區(qū)域性故障已經(jīng)成為整個(gè)Facebook比較關(guān)注的問題,需要有應(yīng)對(duì)自然或人為災(zāi)害的能力。我們對(duì)待故障的處理方式有一個(gè)另外的好處,那就是可以將滾動(dòng)式的軟件升級(jí)模擬成一組可控的單節(jié)點(diǎn)故障,對(duì)這種情況做優(yōu)話意味著我們可以輕而易舉并且很頻繁的做代碼推送。對(duì)于其它故障我們選擇折衷處理,如果故障會(huì)引起數(shù)據(jù)丟失,將優(yōu)先考慮最近數(shù)據(jù)的可用性而不是老數(shù)據(jù),這是因?yàn)閷?duì)歷史數(shù)據(jù)的查詢可以依賴已有的Hbase TSDB,一些自動(dòng)化系統(tǒng)檢測(cè)時(shí)間序列的變化對(duì)部分?jǐn)?shù)據(jù)仍然有用,只要有最新的數(shù)據(jù)產(chǎn)生就會(huì)有新老數(shù)據(jù)比較。
Gorilla通過在不同的數(shù)據(jù)中心中維護(hù)兩個(gè)完全獨(dú)立的實(shí)例,來確保在數(shù)據(jù)中心故障或網(wǎng)絡(luò)分區(qū)情況下的高可用性。一筆數(shù)據(jù)寫入會(huì)流入到每個(gè)Gorilla實(shí)例,而不會(huì)嘗試去保證數(shù)據(jù)的一致性,這就使得大規(guī)模故障比較容易處理。當(dāng)整個(gè)區(qū)域出現(xiàn)故障時(shí),查詢會(huì)指向到其它可用節(jié)點(diǎn),直到之前的節(jié)點(diǎn)已經(jīng)備份了26小時(shí)的數(shù)據(jù)。這對(duì)于處理真實(shí)的或模擬的大規(guī)模故障非常重要,舉個(gè)例子,區(qū)域A的Gorilla實(shí)例完全掛掉了,對(duì)這個(gè)區(qū)域?qū)嵗膶懭牒妥x取會(huì)失敗,失敗的讀取會(huì)透明地路由到健康的區(qū)域B中的實(shí)例。如果故障持續(xù)了很久(超過1分鐘),數(shù)據(jù)將從區(qū)域A中刪除,請(qǐng)求不再會(huì)被重試。發(fā)生這種情況時(shí),區(qū)域A上的所有讀都會(huì)被拒絕,直到區(qū)域A的集群重新健康運(yùn)行26小時(shí),這種處理方式在故障發(fā)生時(shí)可以手動(dòng)或自動(dòng)執(zhí)行。
?
在每個(gè)域內(nèi)都有一個(gè)基于Paxos算法名為ShardManager的系統(tǒng)為節(jié)點(diǎn)分配分片,當(dāng)某個(gè)節(jié)點(diǎn)發(fā)生故障時(shí),ShardManager會(huì)將這個(gè)節(jié)點(diǎn)的分片分發(fā)給集群中的其它節(jié)點(diǎn)。分片在節(jié)點(diǎn)之間遷移時(shí),寫入的數(shù)據(jù)先緩存在客戶端緩沖區(qū),緩沖區(qū)可以保存1分鐘的數(shù)據(jù),超過1分鐘的數(shù)據(jù)將會(huì)被丟棄,以方便為更新的數(shù)據(jù)留出空間。我們發(fā)現(xiàn)大多數(shù)情況下這個(gè)時(shí)長(zhǎng)足夠用來重新分片,而對(duì)于需要消耗更長(zhǎng)時(shí)間的情況,最新的數(shù)據(jù)優(yōu)先級(jí)也更高,因?yàn)閿?shù)據(jù)越新從直觀上看對(duì)操作自動(dòng)檢測(cè)系統(tǒng)越有用。當(dāng)區(qū)域A的一臺(tái)主機(jī)α崩潰或者由于其它任何原因提供不了服務(wù),寫入操作至少會(huì)緩沖1分鐘,這時(shí)Gorilla集群會(huì)嘗試重啟這臺(tái)主機(jī)。如果集群內(nèi)的其它主機(jī)是健康的,故障主機(jī)的分片會(huì)在30秒或更少的時(shí)間內(nèi)發(fā)生遷移,以確保沒有數(shù)據(jù)丟失。如果分片遷移的動(dòng)作沒有及時(shí)發(fā)生,數(shù)據(jù)的讀取將會(huì)被指向到區(qū)域B中的Gorilla實(shí)例上,這個(gè)操作可以通過手動(dòng)或自動(dòng)完成。
當(dāng)分片被分配到某臺(tái)主機(jī)時(shí),會(huì)從GlusterFS讀取全部數(shù)據(jù),這些分片在調(diào)整之前可能是屬于同一主機(jī)。新主機(jī)從GlusterFS讀取和處理完整可用的數(shù)據(jù)大約需要5分鐘時(shí)間,這是因?yàn)橄到y(tǒng)中存儲(chǔ)的shard數(shù)量和總數(shù)據(jù)量的原因,每個(gè)分片標(biāo)志著16GB的磁盤存儲(chǔ),這些數(shù)據(jù)分布在不同的物理機(jī)上,幾分鐘就可以從GlusterFS中讀取出來。當(dāng)主機(jī)正在讀取分片數(shù)據(jù)時(shí),也會(huì)接受新的數(shù)據(jù)寫入,新的寫入會(huì)被放到一個(gè)緩沖隊(duì)列,隊(duì)列中的數(shù)據(jù)會(huì)被盡可能快的處理。分片數(shù)據(jù)處理完成后立刻開始消費(fèi)緩沖隊(duì)列,將數(shù)據(jù)寫到這臺(tái)新的主機(jī)上。回到前面區(qū)域A中的主機(jī)α崩潰的例子:當(dāng)α崩潰時(shí),它的分片被重新分配給同集群的主機(jī)β,一旦β被分配了這些分片就開始接受數(shù)據(jù)寫入,因此從內(nèi)部來看沒有數(shù)據(jù)丟失。如果Gorilla的主機(jī)α能夠以一個(gè)更可控的方式中斷服務(wù),那么在它停服之前就能安全的所有數(shù)據(jù)都刷到磁盤上,所以對(duì)于規(guī)模化的軟件升級(jí)來說也不會(huì)有數(shù)據(jù)丟失。
?
在我們這個(gè)例子中,如果主機(jī)α在數(shù)據(jù)刷盤成功之前掛掉,數(shù)據(jù)就會(huì)丟失。實(shí)際中這種情況很少發(fā)生,即使發(fā)生了通常也僅會(huì)丟失幾秒鐘的數(shù)據(jù)。這種處理方式是我們的一種權(quán)衡,它可以讓集群能有更高的寫入吞吐量,并且在故障停機(jī)之后能夠更快接收最新的數(shù)據(jù)。此外,我們也對(duì)這種情況有監(jiān)控,在故障發(fā)生后能夠?qū)⒆x切到更健康的節(jié)點(diǎn)。
要注意的是,當(dāng)節(jié)點(diǎn)故障時(shí)有些分片可能有部分?jǐn)?shù)據(jù)不可讀,要等到新的節(jié)點(diǎn)將這些分片的數(shù)據(jù)完全從磁盤讀取出來。查詢可能只返回部分?jǐn)?shù)據(jù)(塊文件的讀取順序按時(shí)間從近到遠(yuǎn))并在結(jié)果中標(biāo)記為部分?jǐn)?shù)據(jù)。
當(dāng)處理數(shù)據(jù)讀取的客戶端庫(kù)從區(qū)域A的Gorilla實(shí)例上接收到一個(gè)“部分的”結(jié)果時(shí),它會(huì)從區(qū)域B的實(shí)例中再次讀取那些受影響的時(shí)間序列,如果區(qū)域B有完整的數(shù)據(jù),就使用B的這份數(shù)據(jù)。如果A和B都只有部分?jǐn)?shù)據(jù),會(huì)把這兩部分?jǐn)?shù)據(jù)都返回給調(diào)用者,并在結(jié)果里面做打個(gè)標(biāo),標(biāo)明是因?yàn)槟承╁e(cuò)誤導(dǎo)致數(shù)據(jù)不完整。接下來調(diào)用者可以決定這些數(shù)據(jù)是否有足夠的信息量來繼續(xù)處理請(qǐng)求,或者可以認(rèn)為本次處理失敗。我們做出這樣的選擇是因?yàn)镚orilla最常用于自動(dòng)化系統(tǒng)來檢測(cè)時(shí)間序列的數(shù)據(jù)變化狀況,即使只有部分?jǐn)?shù)據(jù),這些系統(tǒng)也可以運(yùn)行得很好,只要這些數(shù)據(jù)是最近最新的。
?
將讀取從不正常的主機(jī)自動(dòng)轉(zhuǎn)發(fā)到正常運(yùn)行的主機(jī)意味著用戶可以免受重啟和軟件升級(jí)的影響,我們發(fā)現(xiàn)升級(jí)軟件的版本時(shí)不會(huì)導(dǎo)致數(shù)據(jù)丟失,并且在沒有人工干預(yù)的情況下所有的讀取也能繼續(xù)成功執(zhí)行這就使得Gorilla從單機(jī)故障到區(qū)域性故障都能夠透明的提供讀取服務(wù)。
最終,我們?nèi)匀皇褂梦覀兊腍Base TSDB做long-term storage。如果內(nèi)存中所有的數(shù)據(jù)丟失,我們的工程師們?nèi)匀豢梢栽诟映志玫拇鎯?chǔ)系統(tǒng)中繼續(xù)處理數(shù)據(jù)分析和專門的查詢,并且一旦服務(wù)重啟并開始接受新的數(shù)據(jù)寫入,Gorilla就可以繼續(xù)進(jìn)行實(shí)時(shí)數(shù)據(jù)檢測(cè)。
?
5.????Gorilla上的新工具
Gorilla的低延時(shí)查詢特性推動(dòng)產(chǎn)生了一些新的分析工具。
?
5.1?關(guān)聯(lián)引擎
首先是一個(gè)運(yùn)行在Gorilla上的時(shí)間序列關(guān)聯(lián)引擎,關(guān)聯(lián)搜索可以讓用戶對(duì)大量時(shí)間序列做交互式,蠻力搜索,目前限制在每次100萬個(gè)時(shí)間序列。
?
關(guān)聯(lián)引擎將測(cè)試時(shí)間序列和大的時(shí)間序列集做比較來計(jì)算皮爾森產(chǎn)品-時(shí)間相關(guān)系數(shù)(PPMCC)。PPMCC具有在相同形狀走勢(shì)的時(shí)間序列之間找到他們的關(guān)聯(lián)性的能力,無論時(shí)間序列是什么樣的規(guī)模。這大大有助于通過自動(dòng)化方式分析故障的根本原因,并回答“當(dāng)服務(wù)掛掉時(shí)發(fā)生了什么”。我們發(fā)現(xiàn)這種方法能夠帶來比較滿意的結(jié)果,并且比本文末尾引用的文獻(xiàn)中描述的類似方法實(shí)現(xiàn)起來更簡(jiǎn)單。
?
要計(jì)算PPMCC,測(cè)試時(shí)間序列需要和全量時(shí)間序列一起分布在每臺(tái)Gorilla主機(jī)上,然后各個(gè)主機(jī)各自計(jì)算出前N個(gè)有關(guān)聯(lián)關(guān)系的時(shí)間序列,根據(jù)與測(cè)試數(shù)據(jù)相比的PPMCC絕對(duì)值排序,并將時(shí)間序列值返回。在將來,我們希望Gorilla在時(shí)間序列數(shù)據(jù)的監(jiān)控上能拓展出更先進(jìn)的數(shù)據(jù)挖掘技術(shù),例如文末引用的文獻(xiàn)[10,11,16]中描述的分類歸并和異常檢測(cè)技術(shù)。
?
5.2?圖表
低延時(shí)的查詢還能擴(kuò)展出更大查詢量級(jí)的工具。舉個(gè)例子,與監(jiān)控團(tuán)隊(duì)無關(guān)的工程師們創(chuàng)建了一個(gè)新的可視化數(shù)據(jù)界面,它要展示大量線型圖表,而這些圖表數(shù)據(jù)本身就是從大量時(shí)間序列中化簡(jiǎn)計(jì)算來的。這種數(shù)據(jù)可視化方式讓用戶能夠快速直觀的瀏覽大批量數(shù)據(jù),以發(fā)現(xiàn)有異常數(shù)據(jù)值以及與時(shí)間相關(guān)的異常現(xiàn)象
?
5.3 聚合
最近,我們將在后臺(tái)對(duì)數(shù)據(jù)做匯總疊加的程序從一組map-reduce任務(wù)中遷移到了Gorilla上直接執(zhí)行。回想前面對(duì)ODS的介紹,ODS對(duì)老數(shù)據(jù)進(jìn)行基于時(shí)間的聚合(或匯總疊加)壓縮,這種壓縮是有損的,會(huì)讓數(shù)據(jù)之間的間隔度更大,類似于Whisper使用的壓縮格式。在Gorilla之前,map-reduce任務(wù)運(yùn)行在HBase集群上,先讀取出過去一小時(shí)的全部數(shù)據(jù),進(jìn)行計(jì)算,然后輸出到一張新的低粒度的表中。現(xiàn)在,一個(gè)后臺(tái)定時(shí)程序每隔兩小時(shí)掃描全量數(shù)據(jù),再生成新的數(shù)據(jù)到低粒度表中。我們之所以更換實(shí)現(xiàn)方案是因?yàn)樵贕orilla中做全數(shù)據(jù)掃描是非常高效的,方案的更改減少了HBase集群的負(fù)載,我們?cè)僖膊挥脤⑺懈呙芏鹊臄?shù)據(jù)寫到磁盤,并在HBase上執(zhí)行開銷昂貴的全表掃描。
?
6.????經(jīng)驗(yàn)
6.1?容錯(cuò)
我們接下來介紹過去6個(gè)月發(fā)生的幾個(gè)預(yù)期內(nèi)和預(yù)期外的事件,這些事件在一定程度上影響了Facebook站點(diǎn)的可用性,這里我們只限于討論對(duì)Gorilla有影響的事件,因?yàn)槠渌鼏栴}超出了本文的范疇。
網(wǎng)絡(luò)中斷。3起預(yù)期外的發(fā)生在部分機(jī)器的類似網(wǎng)絡(luò)中斷/故障事件,網(wǎng)絡(luò)中斷被自動(dòng)檢測(cè)到,Gorilla自動(dòng)將讀重定向到未受影響的區(qū)域,沒有任何服務(wù)中斷。
應(yīng)對(duì)計(jì)劃內(nèi)的災(zāi)難。1起計(jì)劃內(nèi)的大型消防演練,模擬某個(gè)后端存儲(chǔ)所在處的網(wǎng)絡(luò)全部中斷。根據(jù)上面描述的做法,Gorilla將讀切到未受影響的區(qū)域,一旦故障區(qū)域被恢復(fù),手動(dòng)從日志文件拉取故障時(shí)間段的數(shù)據(jù),從而使故障區(qū)域提供的數(shù)據(jù)面板可以向最終用戶展示預(yù)期內(nèi)的數(shù)據(jù)
配置變更和代碼推送。有6次配置變更和6次代碼發(fā)布需要在重啟指定區(qū)域內(nèi)的Gorilla。
Bug。一個(gè)帶有重大bug的發(fā)布部署到了某個(gè)區(qū)域,Gorilla馬上將負(fù)載轉(zhuǎn)移到其它區(qū)域繼續(xù)提供服務(wù),直到bug解決。在輸出的數(shù)據(jù)中,只有極小的數(shù)據(jù)準(zhǔn)確性問題。
單節(jié)點(diǎn)故障。有5次單機(jī)故障(與上面說的bug無關(guān)),沒有引起數(shù)據(jù)丟失,無需修復(fù)。
過去6個(gè)月,Gorilla沒有出現(xiàn)任何引發(fā)檢測(cè)異常和報(bào)警問題的事故。自從Gorilla推出以來,只有1次事件影響了實(shí)時(shí)監(jiān)控。在任何時(shí)候,持久化存儲(chǔ)為所有與監(jiān)控相關(guān)的查詢扮演備份的角色。
?
6.2?排查和修復(fù)網(wǎng)站故障
關(guān)于Facebook如何使用時(shí)間序列數(shù)據(jù)來支撐業(yè)務(wù)監(jiān)控的例子,可以看看最近一個(gè)依靠監(jiān)控?cái)?shù)據(jù)來快速檢測(cè)和修復(fù)的問題,我們?cè)赟REcon15中首次對(duì)外介紹了這次事件。
一個(gè)神秘的問題導(dǎo)致網(wǎng)站錯(cuò)誤率出現(xiàn)高峰,錯(cuò)誤率上升幾分鐘后在Gorilla可以觀察到異常,這時(shí)由監(jiān)控系統(tǒng)發(fā)出一個(gè)異常警報(bào),幾分鐘后警報(bào)通知到相關(guān)的技術(shù)團(tuán)隊(duì)。然后,辛苦的問題修復(fù)工作開始了,一組工程師緩解了這個(gè)問題,其它人開始尋找問題的根源。通過使用基于Gorilla構(gòu)建的工具,包括前面第5節(jié)介紹的時(shí)間序列關(guān)聯(lián)搜索,他們發(fā)現(xiàn)將發(fā)布的二進(jìn)制包拷貝到Facebook web服務(wù)器這個(gè)常規(guī)流程出了問題,導(dǎo)致整個(gè)網(wǎng)站內(nèi)存使用率下跌,見圖10。問題的檢測(cè),各種各樣的調(diào)試和故障原因分析,依賴于在Gorilla高性能查詢引擎上構(gòu)建的時(shí)間序列分析工具。
?
自從約18個(gè)月前推出以來,Gorilla已經(jīng)幫助Facebook的工程師們識(shí)別和排查出了幾個(gè)類似的生產(chǎn)環(huán)境問題。通過將前90%的查詢的響應(yīng)速度降到10ms,Gorilla也提升了開發(fā)人員的工作效率。另外,現(xiàn)在85%的監(jiān)控?cái)?shù)據(jù)都來自Gorilla,只有少量查詢會(huì)打到HBase TSDB上,這也讓HBase集群負(fù)載變得更低。
?
6.3?經(jīng)驗(yàn)教訓(xùn)
重點(diǎn)考慮最近的數(shù)據(jù)而不是歷史數(shù)據(jù)。Gorilla在優(yōu)化和設(shè)計(jì)定位上比較獨(dú)特,雖然必須表現(xiàn)得非常可靠,但是它不需要ACID規(guī)則來為數(shù)據(jù)做保障。事實(shí)上,我們發(fā)現(xiàn)最近的數(shù)據(jù)在可用性上比過去的數(shù)據(jù)更為重要,這讓我們?cè)谠O(shè)計(jì)上做了比較有意思的權(quán)衡,例如在從磁盤讀取出老數(shù)據(jù)之前保持Gorilla在數(shù)據(jù)讀取上的可用性。
讀取的延時(shí)。高效的壓縮和內(nèi)存級(jí)數(shù)據(jù)結(jié)構(gòu)極大的加快了數(shù)據(jù)讀取的速度,并且促進(jìn)增加了很多使用場(chǎng)景。當(dāng)Gorilla推出時(shí)ODS每秒支撐450次查詢,很快Gorilla就超過了它,目前每秒處理超過5000次常規(guī)查詢業(yè)務(wù),峰值時(shí)達(dá)到每秒40000的查詢,如圖9所示。低延時(shí)的讀取鼓勵(lì)我們的用戶在Gorilla之上構(gòu)建更高級(jí)的數(shù)據(jù)分析工具,如第5節(jié)的描述。
高可用性勝過資源使用效率。容錯(cuò)能力是Gorilla的一個(gè)重要設(shè)計(jì)目標(biāo),它需要具備在不影響數(shù)據(jù)可用性的情況下承受單機(jī)故障的能力。此外,提供的服務(wù)還必須能夠承受可能影響到整個(gè)區(qū)域的災(zāi)難性事件。基于這個(gè)目標(biāo),我們?cè)趦?nèi)存中保存兩份冗余的數(shù)據(jù)副本,即使這樣會(huì)影響資源的使用效率。
?
我們發(fā)現(xiàn)開發(fā)一個(gè)可靠的,有容錯(cuò)能力的系統(tǒng)是整個(gè)項(xiàng)目中最耗時(shí)的部分。雖然開發(fā)團(tuán)隊(duì)在非常短時(shí)間內(nèi)就開發(fā)出了一個(gè)高性能、數(shù)據(jù)壓縮存儲(chǔ)的內(nèi)存級(jí)TSDB原型,但是接下來通過幾個(gè)月的努力工作才讓它具備容錯(cuò)能力。不過當(dāng)系統(tǒng)的生命力在面臨真實(shí)或模擬的故障挑戰(zhàn)時(shí),容錯(cuò)能力帶來的好處是顯而易見的。一個(gè)可以安全重啟,升級(jí),能隨時(shí)新增節(jié)點(diǎn)的系統(tǒng)總能讓技術(shù)人員從中受益。容錯(cuò)能力也讓我們能夠以較低的運(yùn)維成本有效擴(kuò)展Gorilla,同時(shí)為我們的客戶提供高度可靠的服務(wù)。
?
7.????接下來的工作
我們希望通過幾種方式來擴(kuò)展Gorilla。一種方向是在Gorilla內(nèi)存存儲(chǔ)和HBase存儲(chǔ)之間增加一個(gè)更大的基于閃存的二級(jí)存儲(chǔ)。這個(gè)存儲(chǔ)用來存放每?jī)尚r(shí)生成一次的經(jīng)過數(shù)據(jù)壓縮之后的分片,但是總?cè)萘繒?huì)比26小時(shí)更長(zhǎng),我們發(fā)現(xiàn)閃存可以存儲(chǔ)約2周的全量無損的、Gorilla格式壓縮后的數(shù)據(jù),數(shù)據(jù)時(shí)段拉長(zhǎng)對(duì)工程師們排查問題是很有用的。圖8是初步的性能測(cè)試結(jié)果。
?
在創(chuàng)建Gorilla之前,ODS依賴HBase背后的存儲(chǔ)做為實(shí)時(shí)數(shù)據(jù)存儲(chǔ):在數(shù)據(jù)寫入到ODS存儲(chǔ)后很短時(shí)間,需要被用于讀取操作,這給HBase的磁盤I/O帶來了很大的壓力。現(xiàn)在Gorilla充當(dāng)最近數(shù)據(jù)的write-through緩存,在數(shù)據(jù)發(fā)送到ODS存儲(chǔ)后的26小時(shí)內(nèi)都不用從HBase讀取。我們正在利用這個(gè)特點(diǎn)重新調(diào)整數(shù)據(jù)寫入鏈路,讓數(shù)據(jù)在寫入到HBase之前多等待一段時(shí)間,這個(gè)優(yōu)化應(yīng)該會(huì)對(duì)HBase更有效果,但是目前這個(gè)方向還處于早期,沒有相當(dāng)?shù)膶?duì)比數(shù)據(jù)。
?
8.????總結(jié)
Gorilla是我們?cè)贔acebook開發(fā)和部署的一個(gè)新的內(nèi)存時(shí)間序列數(shù)據(jù)庫(kù),Gorilla做為一個(gè)write-through cache,用來收集所有Facebook系統(tǒng)上過去26小時(shí)的監(jiān)控?cái)?shù)據(jù)。在這篇文章中,我們介紹了一種新的壓縮方案,讓我們能夠每分鐘高效的存儲(chǔ)700萬個(gè)數(shù)據(jù)點(diǎn)的監(jiān)控?cái)?shù)據(jù)。此外,與磁盤級(jí)的TSDB相比,Gorilla使我們生產(chǎn)環(huán)境的查詢耗時(shí)縮短了70多倍。基于Gorilla創(chuàng)建了一些新的監(jiān)控工具,包括報(bào)警、自動(dòng)修復(fù)以及一個(gè)在線異常檢查器。Gorilla已經(jīng)部署運(yùn)行了18個(gè)月,在這期間經(jīng)歷了兩次翻倍擴(kuò)容,而沒有太多運(yùn)維上的工作,這證明我們的解決方案具有可擴(kuò)展性。我們還通過幾次大規(guī)模模擬真實(shí)線上故障的演練驗(yàn)證了Gorilla的容錯(cuò)能力 — Gorilla在這些事件中仍然保證了讀寫的高可用,幫助網(wǎng)站在故障中恢復(fù)。
來自 “ ITPUB博客 ” ,鏈接:http://blog.itpub.net/29813872/viewspace-2136365/,如需轉(zhuǎn)載,請(qǐng)注明出處,否則將追究法律責(zé)任。
轉(zhuǎn)載于:http://blog.itpub.net/29813872/viewspace-2136365/
總結(jié)
以上是生活随笔為你收集整理的Facebook TSDB论文翻译的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IP QoS
- 下一篇: insert into on dupli