時序資料庫分析
本文會從時序資料庫的基本概念、使用場景、解決的問題一一展開,最後會從如何解決時序資料儲存這一技術問題入手進行深入分析。
1. 背景
百度無人車在執行時需要監控各種狀態,包括座標,速度,方向,溫度,溼度等等,並且需要把每時每刻監控的資料記錄下來,用來做大資料分析。每輛車每天就會採集將近8T的資料。如果只是儲存下來不查詢也還好(雖然已經是不小的成本),但如果需要快速查詢“今天下午兩點在後廠村路,速度超過60km/h的無人車有哪些”這樣的多緯度分組聚合查詢,那麼時序資料庫會是一個很好的選擇。
2. 什麼是時序資料庫
先來介紹什麼是時序資料。時序資料是基於時間的一系列的資料。在有時間的座標中將這些資料點連成線,往過去看可以做成多緯度報表,揭示其趨勢性、規律性、異常性;往未來看可以做大資料分析,機器學習,實現預測和預警。
時序資料庫就是存放時序資料的資料庫,並且需要支援時序資料的快速寫入、持久化、多緯度的聚合查詢等基本功能。
對比傳統資料庫僅僅記錄了資料的當前值,時序資料庫則記錄了所有的歷史資料。同時時序資料的查詢也總是會帶上時間作為過濾條件。
下面介紹下時序資料庫的一些基本概念(不同的時序資料庫稱呼略有不同)。
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。
3. 時序資料庫的場景
所有有時序資料產生,並且需要展現其歷史趨勢、週期規律、異常性的,進一步對未來做出預測分析的,都是時序資料庫適合的場景。
在工業物聯網環境監控方向,百度天工的客戶就遇到了這麼一個難題,由於工業上面的要求,需要將工況資料儲存起來。客戶每個廠區具有20000個監測點,500毫秒一個採集週期,一共20個廠區。這樣算起來一年將產生驚人的26萬億個資料點。假設每個點50Byte,資料總量將達1P(如果每臺伺服器10T的硬碟,那麼總共需要100多臺伺服器)。這些資料不只是要實時生成,寫入儲存;還要支援快速查詢,做視覺化的展示,幫助管理者分析決策;並且也能夠用來做大資料分析,發現深層次的問題,幫助企業節能減排,增加效益。最終客戶採用了百度天工的時序資料庫方案,幫助他解決了難題。
在網際網路場景中,也有大量的時序資料產生。百度內部有大量服務使用天工物聯網平臺的時序資料庫。舉個例子,百度內部服務為了保障使用者的使用體驗,將使用者的每次網路卡頓、網路延遲都會記錄到百度天工的時序資料庫。由時序資料庫直接生成報表以供技術產品做分析,儘早的發現、解決問題,保證使用者的使用體驗。
4. 時序資料庫遇到的挑戰
很多人可能認為在傳統關係型資料庫上加上時間戳一列就能作為時序資料庫。資料量少的時候確實也沒問題,但少量資料是展現的緯度有限,細節少,可置信低,更加不能用來做大資料分析。很明顯時序資料庫是為了解決海量資料場景而設計的。
可以看到時序資料庫需要解決以下幾個問題
l 時序資料的寫入:如何支援每秒鐘上千萬上億資料點的寫入。
l 時序資料的讀取:又如何支援在秒級對上億資料的分組聚合運算。
l 成本敏感:由海量資料儲存帶來的是成本問題。如何更低成本的儲存這些資料,將成為時序資料庫需要解決的重中之重。
這些問題不是用一篇文章就能含蓋的,同時每個問題都可以從多個角度去優化解決。在這裡只從資料儲存這個角度來嘗試回答如何解決大資料量的寫入和讀取。
5. 資料的儲存
資料的儲存可以分為兩個問題,單機上儲存和分散式儲存。
單機儲存
如果只是儲存起來,直接寫成日誌就行。但因為後續還要快速的查詢,所以需要考慮儲存的結構。
傳統資料庫儲存採用的都是B tree,這是由於其在查詢和順序插入時有利於減少尋道次數的組織形式。我們知道磁碟尋道時間是非常慢的,一般在10ms左右。磁碟的隨機讀寫慢就慢在尋道上面。對於隨機寫入B tree會消耗大量的時間在磁碟尋道上,導致速度很慢。我們知道SSD具有更快的尋道時間,但並沒有從根本上解決這個問題。
對於90%以上場景都是寫入的時序資料庫,B tree很明顯是不合適的。
業界主流都是採用LSM tree替換B tree,比如Hbase, Cassandra等nosql中。這裡我們詳細介紹一下。
LSM tree包括記憶體裡的資料結構和磁碟上的檔案兩部分。分別對應Hbase裡的MemStore和HLog;對應Cassandra裡的MemTable和sstable。
LSM tree操作流程如下:
1. 資料寫入和更新時首先寫入位於記憶體裡的資料結構。為了避免資料丟失也會先寫到WAL檔案中。
2. 記憶體裡的資料結構會定時或者達到固定大小會刷到磁碟。這些磁碟上的檔案不會被修改。
3. 隨著磁碟上積累的檔案越來越多,會定時的進行合併操作,消除冗餘資料,減少檔案數量。
可以看到LSM tree核心思想就是通過記憶體寫和後續磁碟的順序寫入獲得更高的寫入效能,避免了隨機寫入。但同時也犧牲了讀取效能,因為同一個key的值可能存在於多個HFile中。為了獲取更好的讀取效能,可以通過bloom filter和compaction得到,這裡限於篇幅就不詳細展開。
分散式儲存
時序資料庫面向的是海量資料的寫入儲存讀取,單機是無法解決問題的。所以需要採用多機儲存,也就是分散式儲存。
分散式儲存首先要考慮的是如何將資料分佈到多臺機器上面,也就是 分片(sharding)問題。下面我們就時序資料庫分片問題展開介紹。分片問題由分片方法的選擇和分片的設計組成。
分片方法
時序資料庫的分片方法和其他分散式系統是相通的。
雜湊分片:這種方法實現簡單,均衡性較好,但是叢集不易擴充套件。
一致性雜湊:這種方案均衡性好,叢集擴充套件容易,只是實現複雜。代表有Amazon的DynamoDB和開源的Cassandra。
範圍劃分:通常配合全域性有序,複雜度在於合併和分裂。代表有Hbase。
分片設計
分片設計簡單來說就是以什麼做分片,這是非常有技巧的,會直接影響寫入讀取的效能。
結合時序資料庫的特點,根據metric+tags分片是比較好的一種方式,因為往往會按照一個時間範圍查詢,這樣相同metric和tags的資料會分配到一臺機器上連續存放,順序的磁碟讀取是很快的。再結合上面講到的單機儲存內容,可以做到快速查詢。
進一步我們考慮時序資料時間範圍很長的情況,需要根據時間範圍再將分成幾段,分別儲存到不同的機器上,這樣對於大範圍時序資料就可以支援併發查詢,優化查詢速度。
如下圖,第一行和第三行都是同樣的tag(sensor=95D8-7913;city=上海),所以分配到同樣的分片,而第五行雖然也是同樣的tag,但是根據時間範圍再分段,被分到了不同的分片。第二、四、六行屬於同樣的tag(sensor=F3CC-20F3;city=北京)也是一樣的道理。
p5-時序資料分片說明
6. 真實案例
下面我以一批開源時序資料庫作為說明。
InfluxDB:
非常優秀的時序資料庫,但只有單機版是免費開源的,叢集版本是要收費的。從單機版本中可以一窺其儲存方案:在單機上InfluxDB採取類似於LSM tree的儲存結構TSM;而分片的方案InfluxDB先通過+(事實上還要加上retentionPolicy)確定ShardGroup,再通過+的hash code確定到具體的Shard。
這裡timestamp預設情況下是7天對齊,也就是說7天的時序資料會在一個Shard中。
Kairosdb:
底層使用Cassandra作為分散式儲存引擎,如上文提到單機上採用的是LSM tree。
Cassandra有兩級索引:partition key和clustering key。其中partition key是其分片ID,使用的是一致性雜湊;而clustering key在一個partition key中保證有序。
Kairosdb利用Cassandra的特性,將 ++<資料型別>+作為partition key,資料點時間在timestamp上的偏移作為clustering key,其有序性方便做基於時間範圍的查詢。
partition key中的timestamp是3周對齊的,也就是說21天的時序資料會在一個clustering key下。3周的毫秒數是18億正好小於Cassandra每行列數20億的限制。
OpenTsdb:
底層使用Hbase作為其分散式儲存引擎,採用的也是LSM tree。
Hbase採用範圍劃分的分片方式。使用row key做分片,保證其全域性有序。每個row key下可以有多個column family。每個column family下可以有多個column。
上圖是OpenTsdb的row key組織方式。不同於別的時序資料庫,由於Hbase的row key全域性有序,所以增加了可選的salt以達到更好的資料分佈,避免熱點產生。再由與timestamp間的偏移和資料型別組成column qualifier。
他的timestamp是小時對齊的,也就是說一個row key下最多儲存一個小時的資料。並且需要將構成row key的metric和tags都轉成對應的uid來減少儲存空間,避免Hfile索引太大。下圖是真實的row key示例。
p7-open tsdb的row key示例(注3)
7. 結束語
可以看到各分散式時序資料庫雖然儲存方案都略有不同,但本質上是一致的,由於時序資料寫多讀少的場景,在單機上採用更加適合大吞吐量寫入的單機儲存結構,而在分散式方案上根據時序資料的特點來精心設計,目標就是設計的分片方案能方便時序資料的寫入和讀取,同時使資料分佈更加均勻,儘量避免熱點的產生。
資料儲存是時序資料庫設計中很小的一塊內容,但也能管中窺豹,看到時序資料庫從設計之初就要考慮時序資料的特點。後續我們會從其他的角度進行討論。