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