1. 程式人生 > >你需要精通一種監控-時間序列資料庫

你需要精通一種監控-時間序列資料庫

時間序列資料就是歷史烙印,具有不變性,、唯一性、時間排序性

時間序列資料跟關係型資料庫有太多不同,但是很多公司並不想放棄關係型資料庫。 於是就產生了一些特殊的用法,比如用 MySQL 的 VividCortex, 用 Postgres 的 Timescale。 很多人覺得特殊的問題需要特殊的解決方法,於是很多時間序列資料庫從頭寫起,不依賴任何現有的資料庫, 比如 GraphiteInfluxDB

mysql 的引擎,除了常見的 innodb 和 myisam ,還有一個引擎叫 archive ,它的作用和 rrd 差不多,支援插入和查詢操作。

  • 時序資料是基於時間的一系列的資料。在有時間的座標中將這些資料點連成線,往過去看可以做成多緯度報表,揭示其趨勢性、規律性、異常性;往未來看可以做大資料分析,機器學習,實現預測和預警。

  • 時序資料庫就是存放時序資料的資料庫,並且需要支援時序資料的快速寫入、持久化、多緯度的聚合查詢等基本功能。

資料寫入的特點

  • 寫入平穩、持續、高併發高吞吐:時序資料的寫入是比較平穩的,這點與應用資料不同,應用資料通常與應用的訪問量成正比,而應用的訪問量通常存在波峰波谷。時序資料的產生通常是以一個固定的時間頻率產生,不會受其他因素的制約,其資料生成的速度是相對比較平穩的。
  • 寫多讀少:時序資料上95%-99%的操作都是寫操作,是典型的寫多讀少的資料。這與其資料特性相關,例如監控資料,你的監控項可能很多,但是你真正去讀的可能比較少,通常只會關心幾個特定的關鍵指標或者在特定的場景下才會去讀資料。
  • 實時寫入最近生成的資料,無更新:時序資料的寫入是實時的,且每次寫入都是最近生成的資料,這與其資料生成的特點相關,因為其資料生成是隨著時間推進的,而新生成的資料會實時的進行寫入。資料寫入無更新,在時間這個維度上,隨著時間的推進,每次資料都是新資料,不會存在舊資料的更新,不過不排除人為的對資料做訂正。

資料查詢和分析的特點

  • 按時間範圍讀取:通常來說,你不會去關心某個特定點的資料,而是一段時間的資料。
  • 最近的資料被讀取的概率高
  • 歷史資料粗粒度查詢的概率搞
  • 多種精度查詢
  • 多維度分析

資料儲存的特點

  • 資料量大:拿監控資料來舉例,如果我們採集的監控資料的時間間隔是1s,那一個監控項每天會產生86400個數據點,若有10000個監控項,則一天就會產生864000000個數據點。在物聯網場景下,這個數字會更大。整個資料的規模,是TB甚至是PB級的。
  • 冷熱分明:時序資料有非常典型的冷熱特徵,越是歷史的資料,被查詢和分析的概率越低。
  • 具有時效性:時序資料具有時效性,資料通常會有一個儲存週期,超過這個儲存週期的資料可以認為是失效的,可以被回收。一方面是因為越是歷史的資料,可利用的價值越低;另一方面是為了節省儲存成本,低價值的資料可以被清理。
  • 多精度資料儲存:在查詢的特點裡提到時序資料出於儲存成本和查詢效率的考慮,會需要一個多精度的查詢,同樣也需要一個多精度資料的儲存。

開源時間序列資料庫

RRDTool 是最早的時間序列資料庫,它自帶畫圖功能,現在大部分時間序列資料庫都使用Grafana來畫圖。

Graphite 是用 Python 寫的 RRD 資料庫,它的儲存引擎 Whisper 也是 Python 寫的, 它畫圖和聚合能力都強了很多,但是很難水平擴充套件。
OpenTSDB 使用 HBase 解決了水平擴充套件的問題
KairosDB 最初是基於OpenTSDB修改的,但是作者認為相容HBase導致他們不能使用很多 Cassandra 獨有的特性, 於是就拋棄了HBase僅支援Cassandra。
新發布的 OpenTSDB 中也加入了對 Cassandra 的支援。 故事還沒完,Spotify 的人本來想使用 KairosDB,但是覺得專案發展方向不對以及效能太差,就自己擼了一個 Heroic

InfluxDB 早期是完全開源的,後來為了維持公司運營,閉源了叢集版本。 在 Percona Live 上他們做了一個開源資料庫商業模型正面臨危機的演講,裡面調侃紅帽的段子很不錯。 並且今年的 Percona Live 還有專門的時間序列資料庫單元

資料模型

時間序列資料可以分成兩部分

  • 序列 :就是識別符號(維度),主要的目的是方便進行搜尋和篩選
  • 資料點:時間戳和數值構成的陣列
    • 行存:一個數組包含多個點,如 [{t: 2017-09-03-21:24:44, v: 0.1002}, {t: 2017-09-03-21:24:45, v: 0.1012}]
    • 列存:兩個陣列,一個存時間戳,一個存數值,如[ 2017-09-03-21:24:44, 2017-09-03-21:24:45], [0.1002, 0.1012]
      一般情況下:列存能有更好的壓縮率和查詢效能

基本概念

  • metric: 度量,相當於關係型資料庫中的table。
  • data point: 資料點,相當於關係型資料庫中的row。
  • timestamp:時間戳,代表資料點產生的時間。
  • field: 度量下的不同欄位。比如位置這個度量具有經度和緯度兩個field。一般情況下存放的是會隨著時間戳的變化而變化的資料。
  • tag: 標籤,或者附加資訊。一般存放的是並不隨著時間戳變化的屬性資訊。timestamp加上所有的tags可以認為是table的primary key。

如下圖,度量為Wind,每一個數據點都具有一個timestamp,兩個field:direction和speed,兩個tag:sensor、city。它的第一行和第三行,存放的都是sensor號碼為95D8-7913的裝置,屬性城市是上海。隨著時間的變化,風向和風速都發生了改變,風向從23.4變成23.2;而風速從3.4變成了3.3。

 

應用場景

所有有時序資料產生,並且需要展現其歷史趨勢、週期規律、異常性的,進一步對未來做出預測分析的,都是時序資料庫適合的場景。

例:
在工業物聯網環境監控方向,百度天工的客戶就遇到了這麼一個難題,由於工業上面的要求,需要將工況資料儲存起來。客戶每個廠區具有20000個監測點,500毫秒一個採集週期,一共20個廠區。這樣算起來一年將產生驚人的26萬億個資料點。假設每個點50Byte,資料總量將達1P(如果每臺伺服器10T的硬碟,那麼總共需要100多臺伺服器)。這些資料不只是要實時生成,寫入儲存;還要支援快速查詢,做視覺化的展示,幫助管理者分析決策;並且也能夠用來做大資料分析,發現深層次的問題,幫助企業節能減排,增加效益。最終客戶採用了百度天工的時序資料庫方案,幫助他解決了難題。
(這個高逼格)

時序資料庫遇到的挑戰

很多人可能認為在傳統關係型資料庫上加上時間戳一列就能作為時序資料庫。資料量少的時候確實也沒問題,但少量資料是展現的緯度有限,細節少,可置信低,更加不能用來做大資料分析。很明顯時序資料庫是為了解決海量資料場景而設計的。

可以看到時序資料庫需要解決以下幾個問題

  • 時序資料的寫入:如何支援每秒鐘上千萬上億資料點的寫入。

  • 時序資料的讀取:又如何支援在秒級對上億資料的分組聚合運算。

  • 成本敏感:由海量資料儲存帶來的是成本問題。如何更低成本的儲存這些資料,將成為時序資料庫需要解決的重中之重。

這些問題不是用一篇文章就能含蓋的,同時每個問題都可以從多個角度去優化解決。在這裡只從資料儲存這個角度來嘗試回答如何解決大資料量的寫入和讀取。

RRD

RRD (Round Robin Database)資料庫是一個環形的資料庫,資料庫由一個固定大小的資料檔案來存放資料,此資料庫不會像傳統資料庫一樣為隨著資料的增多而檔案的大小也在增加,RRD在建立好後其檔案大小就固定,可以把它想像成一個圓,圓的眾多直徑把圓劃分成一個個扇形,每個扇形就是可以存資料的槽位,每個槽位上被打上了一個時間戳,在圓心上有一個指標,隨著時間的流逝,取回資料後,指標會負責把資料填充在相應的槽位上,當指標轉了360度後,最開始的資料就會被覆蓋,就這樣RRD迴圈填充著資料。

 

image.png


  • 源資料蒐集:採用一些資料蒐集工具,如指令碼、shell命令、SNMP等工具在一定時間間隔裡把資料蒐集填充到rrd資料庫中,這些需要資料蒐集的物件叫DS,一個DS裡在一個時間裡可以蒐集的資料可以有多個,比如一個時間點上對網絡卡來說有進來的流量,也有流出的流量,所以這是2個數據成為一組資料。
  • 臨時儲存:源資料獲取到後是存放在一個數據庫的一個臨時區域,這些源資料叫做PDP
  • 分組-聚合:RRDTool把這些PDP資料作為資料來源通過分組、再利用聚合函式計算後把計算後的結果放在RRD資料庫的時間槽(time slot)上,這些資料叫做CDP,CDP才是RRDTool繪圖時真正打交道的資料,
  • 在從源資料中取資料做聚合計算時會有一個挑選資料的基準,也就是說是以幾個源資料為一組做聚合,根據現實需求的不同,對源資料可以很靈活的選擇不同的時間段提取源資料,再聚合提取不同的聚合值,這樣就產生不同組別的CDP資料,這些有以相同時間段挑選源資料及相同聚合函式計算的結果組成的資料就叫RRA,所以根據挑選源資料的標準及採用的聚合函式的不同,RRA可以有多組。

  • DS:Data Source 資料來源,用於定義蒐集資料的工具所蒐集資料的一些特性
  • Time Solt:時間槽,用於存放通過聚合後的資料區域
  • PDP:Primary Data Point 主資料節點,每個時間點產生的資料,即是蒐集的源資料,沒有做聚合的資料
  • CDP(Consolidation Data Point 聚合資料節點):通過對獲取的源資料分組、聚合計算後得到的資料叫CDP,
  • RRA(Round Robin Archive 輪轉歸檔):以相同的分組、聚合函式計算後的CDP資料組就組成了RRA
  • Resolution(解析度):這是一個時間跨度,表示在做聚合計算時是以幾個連續的time slot裡的資料做聚合,在預設時rrd是以300秒的間隔產生一個time slot。
  • CF:Consolidation Function,合併函式或聚合函式,以RRDTool中有AVERAGE、MAX、MIN、LAST4種

以一個圖來說明PDP、CDP、RRA之間的關係:

 

 

PDP是以規定的時間間隔(預設為300秒)蒐集的源資料,第一個RRA以4個PDP(即4*300秒)為一組做CF後組成的資料,第二個RRA則是以10個PDP為一組做CF後組成的資料。

InfluxDB

InfluxDB 在儲存引擎上糾結了很久, leveldb, rocksdb, boltdb 都玩了個遍,最後決定自己造個輪子叫 Time Structured Merge Tree。

Time Structured Merge Tree (TSM) 和 Log Structured Merge Tree (LSM) 的名字都有點誤導性,關鍵並不是樹,也不是日誌或者時間,而是 Merge。

  • 寫入的時候,資料先寫入到記憶體裡,之後批量寫入到硬碟。
  • 讀的時候,同時讀記憶體和硬碟然後合併結果。
  • 刪除的時候,寫入一個刪除標記,被標記的資料在讀取時不會被返回。
  • 後臺會把小的塊合併成大的塊,此時被標記刪除的資料才真正被刪除
  • 相對於普通資料,有規律的時間序列資料在合併的過程中可以極大的提高壓縮比。

熱點話題

儲存

單機儲存

如果只是儲存起來,直接寫成日誌就行。但因為後續還要快速的查詢,所以需要考慮儲存的結構。
傳統資料庫儲存採用的都是B tree,這是由於其在查詢和順序插入時有利於減少尋道次數的組織形式。我們知道磁碟尋道時間是非常慢的,一般在10ms左右。磁碟的隨機讀寫慢就慢在尋道上面。對於隨機寫入B tree會消耗大量的時間在磁碟尋道上,導致速度很慢。我們知道SSD具有更快的尋道時間,但並沒有從根本上解決這個問題。
對於90%以上場景都是寫入的時序資料庫,B tree很明顯是不合適的。
業界主流都是採用LSM tree替換B tree,比如Hbase, Cassandra等nosql中。這裡我們詳細介紹一下。

LSM tree

LSM tree 包括記憶體裡的資料結構和磁碟上的檔案兩部分,分別對應Hbase裡的MemStore和HLog;對應Cassandra裡的MemTable和sstable
LSM tree操作流程如下:

  1. 資料寫入和更新時首先寫入位於記憶體裡的資料結構。為了避免資料丟失也會先寫到WAL檔案中。

  2. 記憶體裡的資料結構會定時或者達到固定大小會刷到磁碟。這些磁碟上的檔案不會被修改。

  3. 隨著磁碟上積累的檔案越來越多,會定時的進行合併操作,消除冗餘資料,減少檔案數量。

分散式儲存

分散式儲存首先要考慮的是如何將資料分佈到多臺機器上面,也就是 分片(sharding)問題

時序資料庫的分片方法和其他分散式系統是相通的。

  • 雜湊分片:這種方法實現簡單,均衡性較好,但是叢集不易擴充套件。

  • 一致性雜湊:這種方案均衡性好,叢集擴充套件容易,只是實現複雜。代表有Amazon的DynamoDB和開源的Cassandra。

  • 範圍劃分:通常配合全域性有序,複雜度在於合併和分裂。代表有Hbase。

  • 結合時序資料庫的特點,根據metric+tags分片是比較好的一種方式,因為往往會按照一個時間範圍查詢,這樣相同metric和tags的資料會分配到一臺機器上連續存放,順序的磁碟讀取是很快的。

  • 考慮時序資料時間範圍很長的情況,需要根據時間範圍再分成幾段,分別儲存到不同的機器上,這樣對於大範圍時序資料就可以支援併發查詢,優化查詢速度。

如下圖,第一行和第三行都是同樣的tag(sensor=95D8-7913;city=上海),所以分配到同樣的分片,而第五行雖然也是同樣的tag,但是根據時間範圍再分段,被分到了不同的分片。

 

InfluxDB的單機儲存

在單機上InfluxDB採取類似於LSM tree的儲存結構TSM;而分片的方案InfluxDB先通過<database>+<timestamp>(事實上還要加上retentionPolicy)確定ShardGroup,再通過<metric>+<tags>的hash code確定到具體的Shard。

低延遲

時間序列資料庫主要是用來分析的,所以提高響應速度對於診斷生產環境的問題是十分重要的。

把所有資料都放在記憶體

Facebook 寫了叫 Gorilla 的純記憶體時間序列資料庫發表在 VLDB 上,現在已經開源,改名為 Beringei(都是猩猩…)

提前聚合

因為查詢中經常需要對一個很長的時間區間取一些粗粒度的值,比如6月到8月每天的平均CPU使用率。 這些聚合值(均值,最大,最小) 都可以在儲存資料的時候計算出來。BtrDB 和 Akumuli 都在內部節點中儲存聚合值,這樣在很多查詢中底層的節點不需要被訪問就可以得到結果。

處理舊資料

  • 很多時間序列資料都沒有多大用處,特別是當系統長時間正常執行時,完整的歷史資料意義並不大。
  • 所以有些資料庫比如 RDDTool 和 Graphite 會自動刪除高精度的資料,只保留低精度的。
  • 但是對於很多新的時間序列資料庫,在聚合和刪除大量舊資料的同時保證系統正常執行並不像刪除一個本地檔案那樣簡單。
  • 如果監控系統比被監控系統還不穩定就比較尷尬了。

元資料索引

  • 時間序列的識別符號是時間序列資料庫裡主要的元資料。
  • Heroic 使用 Elasticsearch 來儲存元資料, 查詢首先通過 Elasticsearch 來取得符合要求的序列識別符號,之後從 Cassandra 根據識別符號來讀取對應的資料。
  • 但是維護一個完整的搜尋引擎帶來的運維壓力和增加的通訊時間都是不能忽視的。
  • 因此 InfluxDB 和 Prometheus 就自己寫了倒排索引來索引元資料。

Tracing

  • InfluxDB 的人寫了一篇部落格 Metrics are dead, 起因是在一個關於監控的會議 Monitorama 上有人說單純的監控資料已經不能滿足他們複雜的微服務架構了。
  • 於是 InfluxDB 的人反駁說並不是所有人都在使用大規模的分散式系統,對於很多簡單的應用單純的監控資料已經完全夠用了。
  • 我的看法是時間序列資料庫是可以用來存 Trace 的
  • Trace 是更加複雜的時間序列資料,把單純的數值變成一個包含更多資訊的物件,它就是一個 Trace。
  • 並且很多流行的 Tracer 的儲存也是使用 Cassandra, 比如 Zipkin, Uber 的 Jaeger
  • InfluxDB 現在已經支援儲存 Trace 了