0.時序數據庫
時間序列(Time Series):是一組按照時間發生先后順序進行排列的數據點序列,通常一組時間序列的時間間隔為一恆定值(如1秒,5分鍾,1小時等)。
時間序列數據可被簡稱為時序數據。實時監控系統所收集的監控指標數據,通常就是時序數據 。時序數據具有如下特點:
- 每一個時間序列通常為某一固定類型的數值
- 數據按一定的時間間隔持續產生,每條數據擁有自己的時間戳信息
- 通常只會不斷的寫入新的數據,幾乎不會有更新、刪除的場景
- 在讀取上,也往往傾向於讀取最近寫入的數據。
OpenTSDB是其中一種時序數據庫實現,是一種基於Hbase、分布式、可伸縮的時間序列數據庫。
1.OpenTSDB基本概念
- metric:指標名,這個就是我們監控的指標,比如 sys.cpu.user;
- timestamp:時間戳,監控數據產生的時間;
- value:監控值,Long 或者 Double 類型的數據,這個是監控指標在某個時間的具體值;
- tag:標簽,包括標簽名字(tagk)和標簽值(tagv),比如 tagk1=tagv1,主要用於描述數據屬性,每條時序數據必須包含一組和多組的標簽數據。目前 OpenTSDB 最多支持8組標簽。
2.數據樣例:
sys.cpu.user host=webserver01 1356998400 50 sys.cpu.user host=webserver01,cpu=0 1356998400 1 sys.cpu.user host=webserver01,cpu=1 1356998400 0 sys.cpu.user host=webserver01,cpu=2 1356998400 2 sys.cpu.user host=webserver01,cpu=3 1356998400 0 ............ sys.cpu.user host=webserver01,cpu=63 1356998400 1
其中,每一行表示時間序列中的一個DataPoint,每部分對應如下:
每一個Data Point,都關聯一個metrics名稱,但可能關聯多組<tagKey,tagValue>信息。而關於時間序列,事實上就是具有相同的metrics名稱以及相同的<tagKey,tagValue>組信息的Data Points的集合。
3.存儲模型
OpenTSDB是基於HBase進行數據存儲,在HBase中存放tsdb、tsdb-meta、tsdb-tree、tsdb-uid四張表,主要用到的是tsdb、tsdb-uid兩個。
1)tsdb-uid
為了統一各個值的長度以及節省空間,OpenTSDB中為每一個metrics名、tagKey以及tagValue都定義了一個唯一的數字類型的標識碼(Unique Identifier, UID),這些UID信息被保存在OpenTSDB的元數據表tsdb-uid中。同時,為了從UID索引到metrics(或tagKey、tagValue),同時也要從metrics(或tagKey、tagValue)索引到UID,OpenTSDB同時保存了這兩種映射關系數據。
列族Column Family:
在元數據表中,把這兩種數據分別保存到兩個名為"id"與"name"的列族(Column Family)中,Column Family描述信息如下所示:
{NAME => 'id', BLOOMFILTER => 'ROW', COMPRESSION => 'SNAPPY'} {NAME =>'name',BLOOMFILTER => 'ROW', COMPRESSION => 'SNAPPY', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}
OpenTSDB分配UID時遵循如下規則:
- metrics、tagKey和tagValue的UID分別獨立分配
- 每個metrics名稱(或tagKey/tagValue)的UID值都是唯一。不存在不同的metrics(或tagKey/tagValue)使用相同的UID,也不存在同一個metrics(或tagKey/tagValue)使用多個不同的UID
- UID值使用三個字節進行存儲,其范圍是0x000000到0xFFFFFF。
關於metrics名為"cpu.hum",tagKey值為"host",tagValue值分別為"189.120.205.26"、"189.120.205.27"的UID信息定義如下:
說明:
- RowKey為"0"的行中,分別保存了metrics、tagKey和tagValue的當前UID的最大值。當為新的metrics、tagKey和tagValue分配了新的UID后,會更新對應的最大值
- RowKey為"1"的行中,RowKey為UID,Qualifier(列名)為"id:metrics"的值"metrics",Qualifier為"id:tagk"的值為tagKey,Qualifier為id:tagv的值為tagValue
- RowKey為"2"的行中,RowKey為UID,Qualifier為"id:tagv"的值為tagValue,暫不存在UID為"2"的metrics和tagKey
- RowKey為"189.120.205.26"的行中,Qualifer為"name:tagv"的值為UID。表示當"189.120.205.26"為tagValue時,其UID為1
- RowKey為"189.120.205.27"的行中,Qualifer為"name:tagv"的值為UID。表示當"189.120.205.26"為tagValue時,其UID為2
- RowKey為"cpu.hum"的行中,Qualifer為"name:metrics"的值為UID。表示當cpu.hum為metrics時,其UID為1
- RowKey為"host"的行中,Qualifer為"name:tagk"的值為UID。表示當host為tagValue時,其UID為1
由於HBase的存儲數據類型是Bytes,所以UID在存儲時會被轉換為3個字節長度的Bytes數組進行存儲。
TUID:
對每一個Data Point,metrics、timestamp、tagKey和tagValue都是必要的構成元素。除timestamp外,metrics、tagKey和tagValue的UID就可組成一個TSUID,每一個TSUID關聯一個時間序列,如下所示:
<metrics_UID><tagKey1_UID><tagValue1_UID>[...<tagKeyN_UID><tagValueN_UID>]
在上面的例子中就涉及兩個TSUID,分別是:
2)tsdb
tsdb為存放OpenTSDB中所有數據記錄的表,下面具體介紹tsdb的結構設計。
① RowKey設計
tsdb的HBase RowKey中包含主要組成部分為:鹽值(Salt)、metrics名稱、時間戳、tagKey、tagValue等部分。
在tsdb-uid中提到,為了統一各個值的長度以及節省空間,對metrics名稱、tagKey和tagValue分配了UID信息。所以,在HBase RowKey中實際寫入的是metrics UID、tagKey UID和tagValue UID(存放在tsdb-uid中)。
HBase RowKey的數據模型如下圖所示:
- SALT:建議開啟SALT功能,可以有效提高性能。SALT數據的長度是變長的:如果SALT的值值少於256,那么只用一個字節表示即可;如果需要設置更大的SALT值,也會相應地占用更多的空間。
- Metric ID:metrics名經過編碼后,每個Metric ID的長度為三個字節。
- Timestamp:這里整點小時級別的時間戳。
- tagKey UID & tagValue UID:tagKey和tagValue經過編碼后,每個tagKey UID和tagValue UID的長度都為三個字節。tagKey UID和tagValue UID必須成對出現,最少必須存在1對,最多存在8對。
② Qualifier設計
Qualifier用於保存一個或多個DataPoint中的時間戳、數據類型、數據長度等信息。
由於時間戳中的小時級別的信息已經保存在RowKey中了,所以Qualifier只需要保存一個小時中具體某秒或某毫秒的信息即可,這樣可以減少數據占用的空間。
一個小時中的某一秒(少於3600)最多需要2個字節即可表示,而某一毫秒(少於3600000)最多需要4個字節才可以表示。為了節省空間,OpenTSDB沒有使用統一的長度,而是對特定的類型采用特性的編碼方法。Qualifer的數據模型主要分為如下三種情況:秒、毫秒、秒和毫秒混合。
秒類型
當OpenTSDB接收到一個新的DataPoint的時候,如果請求中的時間戳是秒,那么就會插入一個如下模型的數據。
判斷請求中的時間戳為秒或毫秒的方法是基於時間戳數值的大小,如果時間戳的值的超過無符號整數的最大值(即4個字節的長度),那么該時間戳是毫秒,否則為秒。
- Value長度:Value的實際長度是Qualifier的最后3個bit的值加1,即(qualifier & 0x07) + 1。表示該時間戳對應的值的字節數。所以,值的字節數的范圍是1到8個字節。
- Value類型:Value的類型由Qualifier的倒數第4個bit表示,即(qualifier & 0x08)。如果值為1,表示Value的類型為float;如果值為0,表示Value的類型為long。
- 時間戳:時間戳的值由Qualifier的第1到第12個bit表示,即(qualifier & 0xFFF0) >>>4。由於秒級的時間戳最大值不會大於3600,所以qualifer的第1個bit肯定不會是1。
毫秒類型
當OpenTSDB接收到一個新的DataPoint的時候,如果請求中的時間戳是毫秒,那么就會插入一個如下模型的數據。
- Value長度:與秒類型相同。
- Value類型:與秒類型相同。
- 時間戳: 時間戳的值由Qualifier的第5到第26個bit表示,即(qualifier & 0x0FFFFFC0) >>>6。
- 標志位:標志位由Qualifier的前4個bit表示。當該Qualifier表示毫秒級數據時,必須全為1,即(qualifier[0] & 0xF0) == 0xF0。
- 第27到28個bit未使用。
混合類型
當同一小時的數據發生合並后,就會形成混合類型的Qualifier。
合並的方法很簡單,就是按照時間戳順序進行排序后,從小到大依次拼接秒類型和毫秒類型的Qualifier即可。
- 秒類型和毫秒類型的數量沒有限制,並且可以任意組合。
- 不存在相同時間戳的數據,包括秒和毫秒的表示方式。
遍歷混合類型中的所有DataPoint的方法是:
- 從左到右,先判斷前4個bit是否為0xF
- 如果是,則當前DataPoint是毫秒型的,讀取4個字節形成一個毫秒型的DataPoint
- 如果否,則當前DataPoint是秒型的,讀取2個字節形成一個秒型的DataPoint
- 以此迭代即可遍歷所有的DataPoint
③ Value設計
按照時間戳的順序,把多個Value拼接起來的數據模型如下:

每個格子表示一個DataPoint Value的值,這個DataPoint Value的長度可能是1或2或4或8個字節。
DataPoint Value的順序與Qualifier中時間戳的順序一一對應。混合標志:如果最后1個字節為0x01,表示存在秒級類型和毫秒級類型混合的情況。
-
指標名字:這個就是我們監控的指標,比如 sys.cpu.user;
-
時間戳:監控數據產生的時間;
-
值:Long 或者 Double 類型的數據,這個是監控指標在某個時間的具體值;
-
標簽:包括標簽名字(tagk)和標簽值(tagv),比如 tagk1=tagv1,主要用於描述數據屬性,每條時序數據必須包含一組和多組的標簽數據。目前 OpenTSDB 最多支持8組標簽。
參考: