2017年時序數(shù)據(jù)庫忽然火了起來。開年2月Facebook開源了beringei時序數(shù)據(jù)庫;到了4月基于PostgreSQL打造的時序數(shù)據(jù)庫TimeScaleDB也開源了,而早在2016年7月,百度云在其天工物聯(lián)網(wǎng)平臺上發(fā)布了國內(nèi)***多租戶的分布式時序數(shù)據(jù)庫產(chǎn)品TSDB,成為支持其發(fā)展制造,交通,能源,智慧城市等產(chǎn)業(yè)領(lǐng)域的核心產(chǎn)品,同時也成為百度戰(zhàn)略發(fā)展產(chǎn)業(yè)物聯(lián)網(wǎng)的標(biāo)志性事件。時序數(shù)據(jù)庫作為物聯(lián)網(wǎng)方向一個非常重要的服務(wù),業(yè)界的頻頻發(fā)聲,正說明各家企業(yè)已經(jīng)迫不及待的擁抱物聯(lián)網(wǎng)時代的到來。
本文會從時序數(shù)據(jù)庫的基本概念、使用場景、解決的問題一一展開,***會從如何解決時序數(shù)據(jù)存儲這一技術(shù)問題入手進行深入分析。
1. 背景
百度無人車在運行時需要監(jiān)控各種狀態(tài),包括坐標(biāo),速度,方向,溫度,濕度等等,并且需要把每時每刻監(jiān)控的數(shù)據(jù)記錄下來,用來做大數(shù)據(jù)分析。每輛車每天就會采集將近8T的數(shù)據(jù)。如果只是存儲下來不查詢也還好(雖然已經(jīng)是不小的成本),但如果需要快速查詢“今天下午兩點在后廠村路,速度超過60km/h的無人車有哪些”這樣的多緯度分組聚合查詢,那么時序數(shù)據(jù)庫會是一個很好的選擇。
2. 什么是時序數(shù)據(jù)庫
先來介紹什么是時序數(shù)據(jù)。時序數(shù)據(jù)是基于時間的一系列的數(shù)據(jù)。在有時間的坐標(biāo)中將這些數(shù)據(jù)點連成線,往過去看可以做成多緯度報表,揭示其趨勢性、規(guī)律性、異常性;往未來看可以做大數(shù)據(jù)分析,機器學(xué)習(xí),實現(xiàn)預(yù)測和預(yù)警。
時序數(shù)據(jù)庫就是存放時序數(shù)據(jù)的數(shù)據(jù)庫,并且需要支持時序數(shù)據(jù)的快速寫入、持久化、多緯度的聚合查詢等基本功能。
對比傳統(tǒng)數(shù)據(jù)庫僅僅記錄了數(shù)據(jù)的當(dāng)前值,時序數(shù)據(jù)庫則記錄了所有的歷史數(shù)據(jù)。同時時序數(shù)據(jù)的查詢也總是會帶上時間作為過濾條件。
時序數(shù)據(jù)示例
p1-北上廣三地2015年氣溫變化圖
p2-北上廣三地當(dāng)前溫度實時展現(xiàn)
下面介紹下時序數(shù)據(jù)庫的一些基本概念(不同的時序數(shù)據(jù)庫稱呼略有不同)。
metric: 度量,相當(dāng)于關(guān)系型數(shù)據(jù)庫中的table。
data point: 數(shù)據(jù)點,相當(dāng)于關(guān)系型數(shù)據(jù)庫中的row。
timestamp:時間戳,代表數(shù)據(jù)點產(chǎn)生的時間。
field: 度量下的不同字段。比如位置這個度量具有經(jīng)度和緯度兩個field。一般情況下存放的是會隨著時間戳的變化而變化的數(shù)據(jù)。
tag: 標(biāo)簽,或者附加信息。一般存放的是并不隨著時間戳變化的屬性信息。timestamp加上所有的tags可以認(rèn)為是table的primary key。
如下圖,度量為Wind,每一個數(shù)據(jù)點都具有一個timestamp,兩個field:direction和speed,兩個tag:sensor、city。它的***行和第三行,存放的都是sensor號碼為95D8-7913的設(shè)備,屬性城市是上海。隨著時間的變化,風(fēng)向和風(fēng)速都發(fā)生了改變,風(fēng)向從23.4變成23.2;而風(fēng)速從3.4變成了3.3。
p3-時序數(shù)據(jù)庫基本概念圖
3. 時序數(shù)據(jù)庫的場景
所有有時序數(shù)據(jù)產(chǎn)生,并且需要展現(xiàn)其歷史趨勢、周期規(guī)律、異常性的,進一步對未來做出預(yù)測分析的,都是時序數(shù)據(jù)庫適合的場景。
在工業(yè)物聯(lián)網(wǎng)環(huán)境監(jiān)控方向,百度天工的客戶就遇到了這么一個難題,由于工業(yè)上面的要求,需要將工況數(shù)據(jù)存儲起來??蛻裘總€廠區(qū)具有20000個監(jiān)測點,500毫秒一個采集周期,一共20個廠區(qū)。這樣算起來一年將產(chǎn)生驚人的26萬億個數(shù)據(jù)點。假設(shè)每個點50Byte,數(shù)據(jù)總量將達1P(如果每臺服務(wù)器10T的硬盤,那么總共需要100多臺服務(wù)器)。這些數(shù)據(jù)不只是要實時生成,寫入存儲;還要支持快速查詢,做可視化的展示,幫助管理者分析決策;并且也能夠用來做大數(shù)據(jù)分析,發(fā)現(xiàn)深層次的問題,幫助企業(yè)節(jié)能減排,增加效益。最終客戶采用了百度天工的時序數(shù)據(jù)庫方案,幫助他解決了難題。
在互聯(lián)網(wǎng)場景中,也有大量的時序數(shù)據(jù)產(chǎn)生。百度內(nèi)部有大量服務(wù)使用天工物聯(lián)網(wǎng)平臺的時序數(shù)據(jù)庫。舉個例子,百度內(nèi)部服務(wù)為了保障用戶的使用體驗,將用戶的每次網(wǎng)絡(luò)卡頓、網(wǎng)絡(luò)延遲都會記錄到百度天工的時序數(shù)據(jù)庫。由時序數(shù)據(jù)庫直接生成報表以供技術(shù)產(chǎn)品做分析,盡早的發(fā)現(xiàn)、解決問題,保證用戶的使用體驗。
4. 時序數(shù)據(jù)庫遇到的挑戰(zhàn)
很多人可能認(rèn)為在傳統(tǒng)關(guān)系型數(shù)據(jù)庫上加上時間戳一列就能作為時序數(shù)據(jù)庫。數(shù)據(jù)量少的時候確實也沒問題,但少量數(shù)據(jù)是展現(xiàn)的緯度有限,細(xì)節(jié)少,可置信低,更加不能用來做大數(shù)據(jù)分析。很明顯時序數(shù)據(jù)庫是為了解決海量數(shù)據(jù)場景而設(shè)計的。
可以看到時序數(shù)據(jù)庫需要解決以下幾個問題
l 時序數(shù)據(jù)的寫入:如何支持每秒鐘上千萬上億數(shù)據(jù)點的寫入。
l 時序數(shù)據(jù)的讀?。河秩绾沃С衷诿爰墝ι蟽|數(shù)據(jù)的分組聚合運算。
l 成本敏感:由海量數(shù)據(jù)存儲帶來的是成本問題。如何更低成本的存儲這些數(shù)據(jù),將成為時序數(shù)據(jù)庫需要解決的重中之重。
這些問題不是用一篇文章就能含蓋的,同時每個問題都可以從多個角度去優(yōu)化解決。在這里只從數(shù)據(jù)存儲這個角度來嘗試回答如何解決大數(shù)據(jù)量的寫入和讀取。
5. 數(shù)據(jù)的存儲
數(shù)據(jù)的存儲可以分為兩個問題,單機上存儲和分布式存儲。
單機存儲
如果只是存儲起來,直接寫成日志就行。但因為后續(xù)還要快速的查詢,所以需要考慮存儲的結(jié)構(gòu)。
傳統(tǒng)數(shù)據(jù)庫存儲采用的都是B tree,這是由于其在查詢和順序插入時有利于減少尋道次數(shù)的組織形式。我們知道磁盤尋道時間是非常慢的,一般在10ms左右。磁盤的隨機讀寫慢就慢在尋道上面。對于隨機寫入B tree會消耗大量的時間在磁盤尋道上,導(dǎo)致速度很慢。我們知道SSD具有更快的尋道時間,但并沒有從根本上解決這個問題。
對于90%以上場景都是寫入的時序數(shù)據(jù)庫,B tree很明顯是不合適的。
業(yè)界主流都是采用LSM tree替換B tree,比如Hbase, Cassandra等nosql中。這里我們詳細(xì)介紹一下。
LSM tree包括內(nèi)存里的數(shù)據(jù)結(jié)構(gòu)和磁盤上的文件兩部分。分別對應(yīng)Hbase里的MemStore和HLog;對應(yīng)Cassandra里的MemTable和sstable。
LSM tree操作流程如下:
1. 數(shù)據(jù)寫入和更新時首先寫入位于內(nèi)存里的數(shù)據(jù)結(jié)構(gòu)。為了避免數(shù)據(jù)丟失也會先寫到WAL文件中。
2. 內(nèi)存里的數(shù)據(jù)結(jié)構(gòu)會定時或者達到固定大小會刷到磁盤。這些磁盤上的文件不會被修改。
3. 隨著磁盤上積累的文件越來越多,會定時的進行合并操作,消除冗余數(shù)據(jù),減少文件數(shù)量。
p4-Hbase LSM tree結(jié)構(gòu)介紹(注1)
可以看到LSM tree核心思想就是通過內(nèi)存寫和后續(xù)磁盤的順序?qū)懭氆@得更高的寫入性能,避免了隨機寫入。但同時也犧牲了讀取性能,因為同一個key的值可能存在于多個HFile中。為了獲取更好的讀取性能,可以通過bloom filter和compaction得到,這里限于篇幅就不詳細(xì)展開。
分布式存儲
時序數(shù)據(jù)庫面向的是海量數(shù)據(jù)的寫入存儲讀取,單機是無法解決問題的。所以需要采用多機存儲,也就是分布式存儲。
分布式存儲首先要考慮的是如何將數(shù)據(jù)分布到多臺機器上面,也就是 分片(sharding)問題。下面我們就時序數(shù)據(jù)庫分片問題展開介紹。分片問題由分片方法的選擇和分片的設(shè)計組成。
分片方法
時序數(shù)據(jù)庫的分片方法和其他分布式系統(tǒng)是相通的。
哈希分片:這種方法實現(xiàn)簡單,均衡性較好,但是集群不易擴展。
一致性哈希:這種方案均衡性好,集群擴展容易,只是實現(xiàn)復(fù)雜。代表有Amazon的DynamoDB和開源的Cassandra。
范圍劃分:通常配合全局有序,復(fù)雜度在于合并和分裂。代表有Hbase。
分片設(shè)計
分片設(shè)計簡單來說就是以什么做分片,這是非常有技巧的,會直接影響寫入讀取的性能。
結(jié)合時序數(shù)據(jù)庫的特點,根據(jù)metric+tags分片是比較好的一種方式,因為往往會按照一個時間范圍查詢,這樣相同metric和tags的數(shù)據(jù)會分配到一臺機器上連續(xù)存放,順序的磁盤讀取是很快的。再結(jié)合上面講到的單機存儲內(nèi)容,可以做到快速查詢。
進一步我們考慮時序數(shù)據(jù)時間范圍很長的情況,需要根據(jù)時間范圍再將分成幾段,分別存儲到不同的機器上,這樣對于大范圍時序數(shù)據(jù)就可以支持并發(fā)查詢,優(yōu)化查詢速度。
如下圖,***行和第三行都是同樣的tag(sensor=95D8-7913;city=上海),所以分配到同樣的分片,而第五行雖然也是同樣的tag,但是根據(jù)時間范圍再分段,被分到了不同的分片。第二、四、六行屬于同樣的tag(sensor=F3CC-20F3;city=北京)也是一樣的道理。
p5-時序數(shù)據(jù)分片說明
6. 真實案例
下面我以一批開源時序數(shù)據(jù)庫作為說明。
InfluxDB:
非常優(yōu)秀的時序數(shù)據(jù)庫,但只有單機版是免費開源的,集群版本是要收費的。從單機版本中可以一窺其存儲方案:在單機上InfluxDB采取類似于LSM tree的存儲結(jié)構(gòu)TSM;而分片的方案InfluxDB先通過+(事實上還要加上retentionPolicy)確定ShardGroup,再通過+的hash code確定到具體的Shard。
這里timestamp默認(rèn)情況下是7天對齊,也就是說7天的時序數(shù)據(jù)會在一個Shard中。
p6-Influxdb TSM結(jié)構(gòu)圖(注2)
Kairosdb:
底層使用Cassandra作為分布式存儲引擎,如上文提到單機上采用的是LSM tree。
Cassandra有兩級索引:partition key和clustering key。其中partition key是其分片ID,使用的是一致性哈希;而clustering key在一個partition key中保證有序。
Kairosdb利用Cassandra的特性,將++<數(shù)據(jù)類型>+作為partition key,數(shù)據(jù)點時間在timestamp上的偏移作為clustering key,其有序性方便做基于時間范圍的查詢。
partition key中的timestamp是3周對齊的,也就是說21天的時序數(shù)據(jù)會在一個clustering key下。3周的毫秒數(shù)是18億正好小于Cassandra每行列數(shù)20億的限制。
OpenTsdb:
底層使用Hbase作為其分布式存儲引擎,采用的也是LSM tree。
Hbase采用范圍劃分的分片方式。使用row key做分片,保證其全局有序。每個row key下可以有多個column family。每個column family下可以有多個column。
上圖是OpenTsdb的row key組織方式。不同于別的時序數(shù)據(jù)庫,由于Hbase的row key全局有序,所以增加了可選的salt以達到更好的數(shù)據(jù)分布,避免熱點產(chǎn)生。再由與timestamp間的偏移和數(shù)據(jù)類型組成column qualifier。
他的timestamp是小時對齊的,也就是說一個row key下最多存儲一個小時的數(shù)據(jù)。并且需要將構(gòu)成row key的metric和tags都轉(zhuǎn)成對應(yīng)的uid來減少存儲空間,避免Hfile索引太大。下圖是真實的row key示例。
p7-open tsdb的row key示例(注3)
7. 結(jié)束語
可以看到各分布式時序數(shù)據(jù)庫雖然存儲方案都略有不同,但本質(zhì)上是一致的,由于時序數(shù)據(jù)寫多讀少的場景,在單機上采用更加適合大吞吐量寫入的單機存儲結(jié)構(gòu),而在分布式方案上根據(jù)時序數(shù)據(jù)的特點來精心設(shè)計,目標(biāo)就是設(shè)計的分片方案能方便時序數(shù)據(jù)的寫入和讀取,同時使數(shù)據(jù)分布更加均勻,盡量避免熱點的產(chǎn)生。