Durid(二): 數據集及存儲


       druid有三種類型的數據結構: timestamp列,維度列,指標列. 時間撮和指標在底層都是int數組或long數組. 指標值是int或long,而時間撮為long. Segment文件的內部結構可以看做是列式存儲. 每一列的數據都是以不同的數據結果存儲. 通過列式存儲,查詢時只查詢需要的列可以減少延遲. 因為列式存儲,要保存的是某一列的所有行. 所以數組的每一個元素表示的是每一行的這一列的值. 列式存儲還有一個好處是壓縮,當查詢確定要查詢哪些行之后, 首先解壓縮(因為存儲的時候會壓縮),然后直接取出相關的行(定位到數組的指定位置),並運用聚合算子計算指標

目錄

  • 數據集
  • 數據結構
  • segment 

數據集


  • Druid中的數據表(稱為數據源)是一個時間序列事件數據的集合,並分割到一組segment中,而每一個segment通常是0.5-1千萬行。
  • 在形式上,我們定義一個segment為跨越一段時間的數據行的集合。Segment是Druid里面的基本存儲單元,復制和分布都是在segment基礎之上進行的
  • Druid將數據集分成三種類型: Timestamp column, Dimension columns(過濾數據), Metric columns(聚合和計算)
    1. 事件的定義: 時間戳
    2. Dimensions: (things to filter on) 參與事件過濾
    3. Metrics: (things to aggregate over) 要聚合的字段
  • 以下面的事件為例,Druid會將publisher,advertiser,gender,country當做維度列,click和price當做指標列,如下:
    timestamp publisher advertiser gender country click price 2011-01-01T01:01:35Z bieberfever.com google.com Male USA 0 0.65 2011-01-01T01:03:63Z bieberfever.com google.com Male USA 0 0.62 2011-01-01T01:04:51Z bieberfever.com google.com Male USA 1 0.45 2011-01-01T01:00:00Z ultratrimfast.com google.com Female UK 0 0.87 2011-01-01T02:00:00Z ultratrimfast.com google.com Female UK 0 0.99 2011-01-01T02:00:00Z ultratrimfast.com google.com Female UK 1 1.53
  • ruid讀取數據的入口並不會直接存儲原始數據, 而是使用Roll-up這種first-level聚合操作壓縮原始數據,如下:
    timestamp publisher advertiser gender country impressions clicks revenue 2011-01-01T01:00:00Z ultratrimfast.com google.com Male USA 1800 25 15.70 2011-01-01T01:00:00Z bieberfever.com google.com Male USA 2912 42 29.18 2011-01-01T02:00:00Z ultratrimfast.com google.com Male UK 1953 17 17.31 2011-01-01T02:00:00Z bieberfever.com google.com Male UK 3194 170 34.01
  •  用SQL表示類似於對時間撮和所有維度列進行分組,並以原始的指標列做常用的聚合操作

    GROUP BY timestamp, publisher, advertiser, gender, country :: impressions = COUNT(1), clicks = SUM(click), revenue = SUM(price)
  •  為什么不存原始數據? 因為原始數據量可能非常大,對於廣告的場景,一秒鍾的點擊數是以千萬計數. 如果能夠在讀取數據的同時就進行一點聚合運算,就可以大大減少數據量的存儲.這種方式的缺點是不能查詢單條事件,也就是你無法查到每條事件具體的click和price值了.由於后面的查詢都將以上面的查詢為基礎,所以Roll-up的結果一定要能滿足查詢的需求.通常count和sum就足夠了,因此Rollup的粒度是你能查詢的數據的最小時間單位. 假設每隔1秒Rollup一次,后面的查詢你最小只能以一秒為單位,不能查詢一毫秒的事件.默認的粒度單位是ms.


  • Druid的分片是Segment文件. Druid首先總是以時間撮進行分片, 因為事件數據總是有時間撮. 假設以小時為粒度創建下面的兩個Segment文件
  • 在幾乎所有的NoSQL中都有數據分片的概念,比如ES的分片,HBase的Region,都表示的是數據的存儲介質.為什么要進行分片,因為數據大了,不能都存成一個大文件吧,所以要拆分成小文件以便於快速查詢. 伴隨拆分通常都有合並小文件
  • 從Segment文件的名稱可以看出它包含的數據一定是在文件名稱對應的起始和結束時間間隔之內的
  • Segment文件名稱的格式:dataSource_interval_version_partitionNumber.最后一個分區號是當同一個時間撮下數據量超過閾值要分成多個分區了
    1. 分片和分區都表示將數據進行切分. 分片是將不同時間撮分布在不同的文件中, 而分區是相同時間撮放不下了,分成多個分區
    2. 巧合的是Kafka中也有Segment和Partition的概念.Kafka的Partition是topic物理上的分組,一個topic可以分為多個partition,它的partition物理上由多個segment組成.即Partition包含Segment,而Druid是Segment包含Partition.

數據結構


  •  維度列因為要支持過濾和分組,每一個維度列的數據結構包含了三部分:
    1. 值到ID的Map映射
    2.  列的值列表, 存儲的是上一步對應的ID
    3. 倒排索引
  • 示例進行說明:
  • 代表這一維度列的數據結構如下:
    1: Dictionary that encodes column values { "Justin Bieber": 0, "Ke$ha":         1 } 2: Column data [0, 0, 1, 1] 3: Bitmaps - one for each unique value of the column value="Justin Bieber": [1,1,0,0] value="Ke$ha":         [0,0,1,1]
  •  注意: 在最壞情況下前面兩種會隨着數據量的大小而線性增長. 而BitMap的大小則等於數據量大小 * 列的個數.

結構說明


  • 字典表的key都是唯一的, 所以Map的key是unique的column value, Map的value從0開始不斷增加. 示例數據的page列只有兩個不同的值. 所以為Bieber編號0, Ke$ha編號為1.
    Key            |Value ---------------|----- Justin Bieber |0 Ke$ha |1
  •  列的數據: 要保存的是每一行中這一列的值, 值是ID而不是原始的值. 因為有了上面的Map字典, 所以有下面的對應關系,這樣列的值列表直接取最后一列: [0,0,1,1],
rowNum page ID 1 Justin Bieber 0 2 Justin Bieber 0 3       Ke$ha               1 
4       Ke$ha               1

 

  • BitMap的key是第一步Map的key(列的原始值). value數組的每個元素表示指定列的某一行是否包含/存在/等於當前key. 注意: BitMap保存的value數組只有兩個值: 1和0, 1表示這一行包含或等於BitMap的key, 0表示不存在/不包含/不等於,如下:
                            第一行的page列值為Justin Bieber/列值為Justin Bieber的在第一行里 ^
                            | value="Justin Bieber": [1,1,0,0] value="Ke$ha":         [0,0,1,1] ^
                            | 第一行的page列值不是Ke$ha
  • 這種存儲方式, 如果unique重復的列很少,比如page列的每一個值都是不同的. BitMap就會是一個稀疏矩陣.

    A: [1,0,0,0,0,0,0,0,0,0,0] B: [0,1,0,0,0,0,0,0,0,0,0] C: [0,0,1,0,0,0,0,0,0,0,0] D: [0,0,0,1,0,0,0,0,0,0,0] E: [0,0,0,0,1,0,0,0,0,0,0]
  •  unique的重復數量很少也叫做high cardinality,表示基數很高,不同列的數量很多,列值相同的記錄數很少.

  • 稀疏矩陣對於BitMap而言卻是有優點的,因為越是稀疏,它可以被壓縮的比例越大,最后存儲的空間越少(相對原始數據).

  • 上面只是針對page列的BitMap, 對於其他的維度列, 都有自己的BitMap! 即每一個維度列都有一個BitMap.

 

segment


  • 數據進入到Druid首先會進行索引, 這給予了Druid一個機會可以進行分析數據, 添加索引結構, 壓縮, 為查詢優化調整存儲結構
    1. 轉換為列式結構
    2. 使用BitMap索引
    3. 使用不同的壓縮算法
  • 索引的結果是生成Segment文件,Segment中除了保存不同的維度和指標,還保存了這些列的索引信息
  • Druid將索引數據保存到Segment文件中,Segment文件根據時間進行分片. 最基本的設置中, 每一個時間間隔都會創建一個Segment文件
  • 這個時間間隔的長度配置在granularitySpec的segmentGranularity參數.為了Druid工作良好,通常Segment文件大小為300-700M
  • 前面Roll-up時也有一個時間粒度:queryGranularity指的是在讀取時就進行聚合.segmentGranularity則是用於分片進來之后的數據. 


免責聲明!

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



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