https://mp.weixin.qq.com/s?__biz=MzU1NDA4NjU2MA==&mid=2247486232&idx=1&sn=6563cfb8a316cddc7a8e56f58d173305&source=41&pass_ticket=Rs9uTOfpaLsIWWXj3mAn8XMJNs9CoPt%2F%2BiyHgWiP9pwGCiD%2BXutCvUuupl2nqn8u
2017 年時序數據庫忽然火了起來。開年 2 月 Facebook 開源了 beringei 時序數據庫;到了 4 月基於 PostgreSQL 打造的時序數據庫 TimeScaleDB 也開源了,而早在 2016 年 7 月,百度雲在其天工物聯網平台上發布了國內首個多租戶的分布式時序數據庫產品 TSDB,成為支持其發展制造,交通,能源,智慧城市等產業領域的核心產品,同時也成為百度戰略發展產業物聯網的標志性事件。時序數據庫作為物聯網方向一個非常重要的服務,業界的頻頻發聲,正說明各家企業已經迫不及待的擁抱物聯網時代的到來。
本文會從時序數據庫的基本概念、使用場景、解決的問題一一展開,最后會從如何解決時序數據存儲這一技術問題入手進行深入分析。
1. 背景
百度無人車在運行時需要監控各種狀態,包括坐標,速度,方向,溫度,濕度等等,並且需要把每時每刻監控的數據記錄下來,用來做大數據分析。每輛車每天就會采集將近 8T 的數據。如果只是存儲下來不查詢也還好(雖然已經是不小的成本),但如果需要快速查詢“今天下午兩點在后廠村路,速度超過 60km/h 的無人車有哪些”這樣的多緯度分組聚合查詢,那么時序數據庫會是一個很好的選擇。
2. 什么是時序數據庫
先來介紹什么是時序數據。時序數據是基於時間的一系列的數據。在有時間的坐標中將這些數據點連成線,往過去看可以做成多緯度報表,揭示其趨勢性、規律性、異常性;往未來看可以做大數據分析,機器學習,實現預測和預警。
時序數據庫就是存放時序數據的數據庫,並且需要支持時序數據的快速寫入、持久化、多緯度的聚合查詢等基本功能。
對比傳統數據庫僅僅記錄了數據的當前值,時序數據庫則記錄了所有的歷史數據。同時時序數據的查詢也總是會帶上時間作為過濾條件。
時序數據示例
p1- 北上廣三地 2015 年氣溫變化圖
p2- 北上廣三地當前溫度實時展現
下面介紹下時序數據庫的一些基本概念(不同的時序數據庫稱呼略有不同)。
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。
p3- 時序數據庫基本概念圖
3. 時序數據庫的場景
所有有時序數據產生,並且需要展現其歷史趨勢、周期規律、異常性的,進一步對未來做出預測分析的,都是時序數據庫適合的場景。
在工業物聯網環境監控方向,百度天工的客戶就遇到了這么一個難題,由於工業上面的要求,需要將工況數據存儲起來。客戶每個廠區具有 20000 個監測點,500 毫秒一個采集周期,一共 20 個廠區。這樣算起來一年將產生驚人的 26 萬億個數據點。假設每個點 50Byte,數據總量將達 1P(如果每台服務器 10T 的硬盤,那么總共需要 100 多台服務器)。這些數據不只是要實時生成,寫入存儲;還要支持快速查詢,做可視化的展示,幫助管理者分析決策;並且也能夠用來做大數據分析,發現深層次的問題,幫助企業節能減排,增加效益。最終客戶采用了百度天工的時序數據庫方案,幫助他解決了難題。
在互聯網場景中,也有大量的時序數據產生。百度內部有大量服務使用天工物聯網平台的時序數據庫。舉個例子,百度內部服務為了保障用戶的使用體驗,將用戶的每次網絡卡頓、網絡延遲都會記錄到百度天工的時序數據庫。由時序數據庫直接生成報表以供技術產品做分析,盡早的發現、解決問題,保證用戶的使用體驗。
4. 時序數據庫遇到的挑戰
很多人可能認為在傳統關系型數據庫上加上時間戳一列就能作為時序數據庫。數據量少的時候確實也沒問題,但少量數據是展現的緯度有限,細節少,可置信低,更加不能用來做大數據分析。很明顯時序數據庫是為了解決海量數據場景而設計的。
可以看到時序數據庫需要解決以下幾個問題
-
時序數據的寫入:如何支持每秒鍾上千萬上億數據點的寫入。
-
時序數據的讀取:又如何支持在秒級對上億數據的分組聚合運算。
-
成本敏感:由海量數據存儲帶來的是成本問題。如何更低成本的存儲這些數據,將成為時序數據庫需要解決的重中之重。
這些問題不是用一篇文章就能涵蓋的,同時每個問題都可以從多個角度去優化解決。在這里只從數據存儲這個角度來嘗試回答如何解決大數據量的寫入和讀取。
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 操作流程如下:
-
數據寫入和更新時首先寫入位於內存里的數據結構。為了避免數據丟失也會先寫到 WAL 文件中。
-
內存里的數據結構會定時或者達到固定大小會刷到磁盤。這些磁盤上的文件不會被修改。
-
隨着磁盤上積累的文件越來越多,會定時的進行合並操作,消除冗余數據,減少文件數量。
p4-Hbase LSM tree 結構介紹(注 1)
可以看到 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 中。
p6-Influxdb TSM 結構圖(注 2)
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. 結束語
可以看到各分布式時序數據庫雖然存儲方案都略有不同,但本質上是一致的,由於時序數據寫多讀少的場景,在單機上采用更加適合大吞吐量寫入的單機存儲結構,而在分布式方案上根據時序數據的特點來精心設計,目標就是設計的分片方案能方便時序數據的寫入和讀取,同時使數據分布更加均勻,盡量避免熱點的產生。
數據存儲是時序數據庫設計中很小的一塊內容,但也能管中窺豹,看到時序數據庫從設計之初就要考慮時序數據的特點。后續我們會從其他的角度進行討論。
注 1:來源 http://tristartom.github.io/research.html
注 2:來源 http://blog.fatedier.com/2016/08/05/detailed-in-influxdb-tsm-storage-engine-one/
注 3:來源 http://opentsdb.net/docs/build/html/user_guide/backends/hbase.html