時序數據庫經常應用於機房運維監控、物聯網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,非常高效的實現了在一段時間內,按照某個緯度的數據抓取、聚合與分析,這又恰恰是時序應用場景的核心需求,能極大提升整體計算資源的應用效率。
-----------------------------------------------------------------------------------------------
本文由公眾號「守護石」出品