時序資料庫經常應用於機房運維監控、物聯網IoT裝置採集儲存、網際網路廣告點選分析等基於時間線且多源資料連續湧入資料平臺的應用場景,InfluxDB專為時序資料儲存而生,尤其是在工業領域的智慧製造,未來應用潛力巨大。
資料模型
1.時序資料的特徵
時序資料應用場景就是在時間線上每個時間點都會從多個資料來源湧入資料,按照連續時間的多種緯度產生大量資料,並按秒甚至毫秒計算的實時性寫入儲存。
傳統的RDBMS資料庫對寫入的支援都是按行處理,並建立B樹結構的索引,它並不是為了批量高速寫入而設計,尤其像多緯度時序資料連續的湧入資料平臺,RDBMS的儲存引擎必然導致負載、吞吐在寫入效能上的極不適應。
因此時序資料的儲存設計一般不會考慮傳統RDBMS,都會將目光放在以LSM-Tree以及列式的資料結構儲存方向。
LSM資料模型是對批量資料從記憶體到磁碟檔案的層層下壓,有些會對資料KV順序排列,形成列簇存放檔案,例如HBase,Cassandra;有些按列欄位對應多個檔案儲存,形成列式儲存,這樣可以極大提升壓縮率,例如Druid.io。
再看時序資料結構:資料來源(DataSource)+指標項(Metric)+時間戳(TimeStamp)=資料點,每個資料點就是時間線上的一個指標測量點。
如果從一個二維圖來表示的話,就如同下圖所示,橫軸代表了時間,縱軸代表了測量值,而圖中的每個點就是指標測量的資料點(Point)了。
上圖可以表述為:資料來源1(動環檢測-高新區機房-資料區)—指標(溼度),資料來源2(動環檢測-西鹹新區機房-計算區)—指標(溼度)在連續時間點(TimeStamp)上的兩條時序折線圖。
其中動環檢測代表了資料來源的業務領域,機房、區域代表了資料來源的標籤(Tag),動環檢測+機房+區域就確定了唯一的時序資料來源。
2.基於HBase的OpenTSDB資料模型
時序資料庫除了InfluxDB之外,還有另一個比較有名的時序庫—OpenTSDB,OpenTSDB就是基於HBase平臺的時序資料庫實現,在我以前的回答中多次分析過HBase的內部機制,它的特徵就是源自Google BigTable的資料模型,以列簇為資料組織和儲存的整體單元。
我們可以理解列簇就是一張Excel寬表,表中每一個單元格就是由K/V組成,KEY由行鍵+列簇名+列名+時間等構成,那麼對KEY排序後,類似單元格的K/V自然就形成了按照行鍵、列簇名、列名、時間的順序排列。非常方便地按照行鍵去抓取列簇的一組列值。
我們從時序資料的特徵可知,資料來源+指標+時間戳就可以確定到一個資料點,那麼在OpenTSDB的設計中,這個組合就是行鍵。不過由於時間戳是連續不同的,就會導致每個K/V的行鍵都不同,這會產生非常大量的單列資料。
因此OpenTSDB進行了優化,將行鍵中的時間戳按照小時計,按照秒劃分為3600個列,這樣列簇的一行代表了一個小時的時序資料,每一列代表那一秒的值。
從機制設計的角度看,OpenTSDB基於HBase的特性進行優化,已經做得很好了。但是HBase的本質還是K/V作為一個原子單位,由多個K/V形成Block,再由Block形成HFile檔案。
產生的缺點是:
(1)每一個K/V都會因為KEY的構建帶來大量的冗餘,而且無法有效實現基於標籤tag的條件索引,因為tag都揉進了行鍵之中,這就需要全量順序掃描。
(2)KEY是無法在通用的壓縮演算法上進行有效壓縮的,最終一定會佔用更多的儲存成本,本質問題還在於無法對時間戳(Timestamp)進行獨立剝離處理。
3.InfluxDB資料模型
InfluxDB並沒有打算完全搞一套全新的資料儲存理論體系,而是在參考HBase的LSM-Tree資料模型後,建立一套適合時序資料的儲存架構,名叫TSM。
我們再重複一下HBase的資料模型機制,追加WAL,在記憶體中建立批量寫入的資料緩衝區MemStore,定期或寫滿的情況下MemStore沖刷(Flush)到磁碟StoreFile,StoreFile再定期進行檔案合併,做歸併排序並完成記錄去重。
這種基於LSM-Tree結構的資料模型能極大提升寫入的效能,很多NoSQL系統都是這個操作路子。
InfluxDB也不例外,繼續這種LSM-Tree結構套路,時序資料寫入時先追加WAL,然後再寫入Cache,然後定期或寫滿的情況下衝刷(Flush)到磁碟TSM檔案。
我們看它跟HBase不同的地方,主要在於資料結構的設計,HBase的MemStore對寫入的資料直接封裝成K/V然後組成一個個更大的Chunk塊。
這種結構最大的特點就是HBase以一種近乎於通用的方式順序地將原子單元(K/V)排列並封裝成更大的Chunk塊,資料之間設計得非常鬆散,隨機性查詢不依賴資料結構優化,而是依賴索引基礎上的掃描,屬於萬金油型,任何上層應用都可以拿來一用,例如:各種寬表業務的掃描查詢、聚合分析或者設計出按時間線分析的TSDB。
但是InfluxDB在Cache中重新優化了結構:Map集合<資料來源,Map集合<指標,List列表<時間戳:資料值>>>,我們可以看到是一個Map套Map再套List的結構,第一層Map就是序列(Series)集合,Series就是InfluxDB定義的資料來源(表measurement + 多標籤tagset),第二層Map為指標集合,InfluxDB定義指標為Fields,第三層就是某個資料來源的某個指標的時間線記錄列表了。
因此我們可以看到InfluxDB首先根據時序資料的特徵,進行了資料結構上的重新調整來適應這種時序特徵的需要,那麼就有了以資料來源為索引去定位指標,以指標為索引去定位時間線上的資料列表。
InfluxDB記憶體中Cache會Flush到TSM檔案,TSM檔案中也會根據上述結構建立磁碟檔案的資料塊排列和索引塊排列,每個資料塊就可以分別是Timestamps列表和Values列表組成。
那麼就可以對TimeStamps列表進行單獨壓縮(delta-delta編碼),Values列表具有相同的型別Type,可以根據Type進行壓縮。索引塊建立資料塊與Series之間的關係,並通過對時間範圍的二分查詢的方式快速定位待查資料的資料塊位移。
4.InfluxDB索引
InfluxDb分為兩種索引,一種是TSM檔案內建的Series索引,另一種就是倒排索引,
Series索引:在InfluxDB的內建索引中Series + field就是主鍵,定位到某項指標的一個索引塊,索引塊由N個索引實體組成,每個索引實體提供了最小時間和最大時間,以及這個時間範圍對應到TSM檔案Series資料塊的偏移量,TSM檔案有多個Series資料塊組成,每個Series資料塊就包含了時間列表(Timestamps),索引實體的最小時間和最大時間就對應了這個時間列表。因此Series索引塊就可以通過Key排序,並通過時間範圍進行二分查詢,先快速定位某個時間範圍上的時序資料,然後再做掃描匹配。
倒排索引:InfluxDB除了TSM結構之外,還有TSI結構,TSI主要是進行時序資料的倒排索引,例如:通過動環檢測-高新區機房為條件查詢高新區機房各個區域的各項指標,或者也可以通過動環檢測-資料區域為條件查詢所有機房的資料區域的各項指標。
TSI的資料結構為:Map集合<標籤名,Map集合<標籤值,List列表
通過這種索引方式就能實現通過標籤快速索引包含此標籤的所有資料來源。通過資料來源的Series,再從TSM檔案中按其他條件查詢。
TSI資料模型使用了與TSM一樣的套路,本質上都是基於LSM資料結構,伴隨資料寫入,倒排索引先寫WAL,在寫記憶體In-Memory Index,當達到記憶體閥值後Flush倒TSI檔案,而TSI檔案就是基於磁碟的Disk-Based Index的倒排索引檔案,裡面包含了Series塊和標籤塊,可以通過標籤塊中的標籤值在Series塊中找到對應的所有Series。
時序庫在這種按資料來源標籤(Tag)分類的分析查詢上會表現得非常高效,這也是InfluxDB充分應對了時序資料業務的需要。
分散式
InfluxDB在叢集方面閉源了,這的確是件非常可惜的事情。不過可以理解,任何技術創業公司都要謀求生存和發展,商業輸血是必須的,希望有一天InfluxDB的母公司能被巨頭收購,然後再次完全開源。我在以前專門講過一期HBase和Cassandra的對比,有興趣的朋友可以看看:HBase 與 Cassandra 架構對比分析的經驗分享
InfluxDB更傾向於去中心化的分散式,裡面有我對兩者分散式的對比,而InfluxDB更接近於Cassandra,Cassandra是一套去中心化的分散式架構,而InfluxDB包括了Meta節點和Data節點,但是Meta節點看似是主節點,但作用很薄弱,主要儲存一些公共資訊和連續查詢排程,資料讀寫問題主要還是集中在Data節點,而Data節點之間的讀寫,更貼近於去中心化的方式。
InfluxDB是CAP定理中的AP分散式系統,非常側重於高可用,而不是一致性。其次InfluxDB的主要是兩級分片,第一級為ShardGroup,次一級是Shard。
ShardGroup是個邏輯概念,代表一個時間段內的多個Shard,在建立InfluxDb資料庫(DataBase)和保留策略(RETENTION POLICY)後就確定了ShardGroup的連續時間段,這就相當於對時序資料首先進行了按照時間段範圍的分割槽,例如1個小時作為一個ShardGroup。
但是這1個小時內的資料並不是儲存在一個資料節點上,這點就大大不同於HBase的Region了,Region首先會朝一個資料節點使勁地寫,寫飽了才進行Region拆分,然後實現Region遷移分佈。
而InfluxDB應該是參考了Cassandra那一套辦法,使用了基於Series作為Key的Hash分佈,將一個ShardGroup的多個Shard分佈在叢集中不同的資料節點上。
下面定位shard的程式碼中key就是Series,也就是唯一指定的資料來源(表measurement +標籤集合 tagset)。
shard := shardGroup.shards[fnv.New64a(key) % len(shardGroup.Shards)]
上面程式碼中shardGroup.Shards就是ShardGroup的Shard數量,該數量為N/X,N是叢集中的資料節點數量,X就是副本因子。
例如:叢集有4個節點,2個副本,4/2就得到了需要將1個小時的ShardGroup範圍資料拆成2個Shard,並各複製2份,分佈在四個資料節點,就是通過這種Hash分佈方式,更均勻的分佈了時序資料,也充分利用叢集每一個資料節點的讀寫優勢。
InfluxDB是最終一致性,在寫入一致性上實現了各種調節策略Any、One、Quorum、All,和Cassandra如出一轍,並且加強了協調節點的Hinted handoff的臨時佇列持久化作用,這就是完全為了高可用性。
即便真正的副本節點故障了,就先在協同節點的Hinted handoff佇列中持久化儲存,等到副本節點故障恢復後,再從Hinted handoff佇列中複製恢復。就是為了達到最終一致性。
另外InfluxDB和Cassandra3.x及以前版本一樣,具有後臺碎片修復的反熵功能(anti-Entrory),不過有意思的地方是Cassandra在新的4.0版本已經不在保留後臺讀修復的功能了,而且之前版本也不推薦啟用,因為具有了主動讀修復能力,後臺讀修復作用不大,而且還影響效能。
當然InfluxDB是不是和Cassandra一樣,在讀取的過程中進行了主動讀修復,因為是閉源系統,這點上我還不太清楚。
InfluxDB在刪除問題上會表現的非常薄弱,只允許刪除一個範圍集合或者Series下的一個維度集合,這也是這種時序結構的設計使然,對單個Point的刪除會很麻煩,成本很大。
刪除方法上應該也是參考了Cassandra的Tombstone機制:去中心化的問題就是怕大家都刪除副本後,某個正好處於故障中的副本節點又恢復了,它不知道發生了什麼事情,但修復過程中它會認為被刪除的副本大家弄丟了,而去讓大家恢復。
有了Tombstone標記的長期存在,那麼存在副本資料的故障節點,當恢復後根據其他副本節點Tombstone標記,也就知道自己故障期間曾經發生了刪除的情況,馬上進行自身對應副本資料的刪除。
總結
首先Influxdb的出現,使得時序資料的儲存體量大大減少,這極為有利於物聯網時代的資料儲存問題,關鍵是合理的儲存結構設計,一方面能減少資料冗餘量,另一方面能充分利用特定的壓縮演算法,例如:delta-delta壓縮演算法對TSM檔案中時間戳集合的高度壓縮;其次我們在面對時間為主線的儲存上,以前很難找到合適的方案,例如我們通過使用Elasticsearch建立日期索引來儲存日誌,但是我們總是在什麼條件下建立,什麼時候銷燬的問題上糾結,而在編碼層面費很大力氣,但是Influxdb的保留策略與分割槽分組很好解決了這類問題;
最後就是更為合適時序模型的索引建立,尤其是倒排索引TSI,非常高效的實現了在一段時間內,按照某個緯度的資料抓取、聚合與分析,這又恰恰是時序應用場景的核心需求,能極大提升整體計算資源的應用效率。
------------------------------------------------------------------------------------------------------------
本文由西安守護石資訊科技的 CTO 老方發表,轉載請註明來源和作者。