上一篇文章《OpenTSDB原理系列-中繼資料模型》講到了OpenTSDB的基礎概念,以及中繼資料模型,這篇文章介紹OpenTSDB的HBase資料表設計。OpenTSDB共涉及兩種類型的資料:Metrics資料以及Annotation(注釋)資料,在将這些資料存到HBase表中時,針對RowKey, Qualifier以及Value資訊都做了特殊設計,進而使得存儲更加高效。
Metrics RowKey設計
metrics資料的HBase RowKey中包含主要組成部分為:鹽值(Salt)、metrics名稱、時間戳、tagKey、tagValue等部分。上篇文章已經講到,為了統一各個值的長度以及節省空間,對metrics名稱、tagKey和tagValue配置設定了UID資訊。是以,在HBase RowKey中實際寫入的metrics UID、tagKey UID和tagValue 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對。
Metrics 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
Metrics 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,表示存在秒級類型和毫秒級類型混合的情況。
Annotation資料
Annotation用于描述某一個時間點發生的事件,Annotation的資料為字元串類型,這與數字類型的metrics資料并不同。
注意:
- Annotation資料隻支援秒級時間戳的資料。
- Annotation資料不會合并。
Annotation RowKey設計
RowKey的資料模型如下圖:
- SALT/ Timestamp/Metric UID/ tagKey UID /tagValue UID的意義與metrics RowKey中的意義相同。
- 把[Metric UID/ tagKey UID /tagValue UID]部分統稱為TSUID。實際上,讀寫注釋資料的時候,需要指定的是TSUID,而不是像metrics資料中那樣分開指定的。
Annotation Qualifier設計
由于注釋資料隻支援秒級類型的資料,同時注釋類型的資料不支援合并,是以Qualifier的設計相對metrics資料簡單一些。Qualifier定義如下:
- 與metrics資料的Qualifier相比,注釋資料的HBase Qualifer的長度是3個位元組。
- 标志位:使用第1個位元組表示,而且值必須為0x01。即(qualifier & 0xFF0000)>>>16 == 0x01。
- 時間戳:使用第2到第3個位元組表示。即時間戳的值為(qualifier & 0x00FFFF)。
Annotation Value設計
注釋資料中的Value儲存的是字元串類型的資料,整個HBase Value部分就是注釋資料的值。
Append模式
當OpenTSDB啟動APPEND模式後,每個插入的新DataPoint,都會以HBase的append的方式寫入。
注意:
- 由于使用了HBase的append的接口,每次插入一個新資料,都需要對同一小時的資料都執行一次讀取和插入的操作;另外多線程對同一小時的資料進行更新的時候,是不能并發的。這樣就大大限制了資料寫入的速度了,一般情況下不建議使用這種模式。
- append的資料其實就是合并過的資料了,是以不會參與OpenTSDB的Compaction流程。
Append模式RowKey設計
Append模式的RowKey設計與普通模式下寫入的metrics資料的RowKey是相同的。
Append模式Qualifier設計
Append模式下,由于同1小時的資料中不存在多個Qualifier,是以隻需要使用一個固定的Qualifier即可。
- Append模式的Qualifier使用3個位元組表示
- 标志位: 由第1個位元組表示,而且值必須為0x05。即(qualifier & 0xFF0000)>>>16 == 0x05
- 固定部分:由第2到第3個位元組表示,這部分的值固定為0x0000,是以,Append模式的Qualifier固定為0x050000
Append模式Value設計
Append模式下, Value部分既要儲存時間戳,數值類型和數值長度,也要儲存對應的數值。
Value的資料結構如下:
- 上圖每一個方塊表示的Qualifier與Value的定義,與普通寫入模式下的定義相同
- 周遊Value中的所有DataPoint的方法是:
- 從左到右,先判斷前4個bit是否為0xF
- 如果是,則目前DataPoint是毫秒型的讀取4個位元組形成一個毫秒型的Qualifier,從Qualifier中獲得Value的長度,然後再讀取對應長度的位元組數
- 如果否,則目前DataPoint是秒型的,讀取2個位元組形成一個秒型的Qualifier,從Qualifier中獲得Value的長度,然後再讀取對應長度的位元組數;
- 依此疊代即可周遊所有的DataPoint。
本文首發于:NoSQL漫談(nosqlnotes.com)
歡迎掃描關注公衆号"NoSQL漫談":