OpenTSDB在HBase中的底層數據結構設計


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設計

HBase Value部分用於保存一個或多個DataPoint的 具體某個時間戳對應的值
由於在Qualifier中已經保存了DataPoint Value的類型和DataPoint Value的長度,所以無論是秒級還是毫秒級的值,都可以用相同的表示方法,而混合類型就是多個DataPoint Value的拼接。
HBase Value按照長度可以分為如下幾種類型:
單字節:當DataPoint Value為long型,且大於等於-128(Byte.MIN_VALUE),且少於或等於127(Byte.MAX_VALUE)的時候,使用1個字節存儲。
兩字節:當DataPoint Value為long型,且大於等於-32768(Short.MIN_VALUE),且少於或等於32767(Short.MAX_VALUE)的時候,使用2個字節存儲。
四字節:當DataPoint Value為long型,且大於等於0x80000000(Integer.MIN_VALUE),且少於或等於0x7FFFFFFF(Integer.MAX_VALUE)的時候,使用4個字節存儲。
八字節:當DataPoint Value為long型,且不是上面三種類型的時候,使用8個字節存儲。當DataPoint Value為float型的時候,使用8個字節表示。
多字節:
按照時間戳的順序,把多個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組標簽。 

 

參考:

 

OpenTSDB原理系列-元數據模型

OpenTSDB原理系列-TSDB數據表設計

OpenTSDB的數據模型


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM