Apache Druid 中文翻譯 - 段(Segments)

Segments

Apache Druid將其索引(index)存儲在(Segments)文件中,該段文件按時間進行分區(partitioned)。在默認設置中,Druid爲每個時間間隔(time interval)創建一個段文件,其中時間間隔可通過配置 granularitySpec.segmentGranularity實現。爲了使Druid在繁重的查詢負載下正常運行,將段文件的大小配置爲建議的300MB-700MB範圍內很重要。如果你的段文件超過該範圍,然後再考慮要麼改變時間間隔(time interval)的粒度或分區你的數據(partitioning your data),並調整了您的partitionsSpec.targetPartitionSize (建議將參數調整爲500萬行)。有關更多信息,請參見下面的分片部分和“ 批量提取”文檔的“分區規範”部分。

A segment file's core data structures 段文件的核心數據結構

 下面,我們描述了段文件的內部結構,該結構基本上是柱狀的:每列的數據都佈置在單獨的數據結構中。通過分別存儲每列,Druid可以通過僅掃描查詢實際需要的那些列來減少查詢延遲。共有三種基本列類型:時間戳列,維度列和指標列,如下圖所示:

德魯伊列類型

timestamp和metric列很簡單:在幕後每個都是由LZ4算法壓縮的整數或浮點值的數組。一旦查詢知道需要選擇的行,它就簡單地解壓縮這些行,取出相關的行,然後應用所需的聚合運算符。與所有列一樣,如果查詢不需要一列,則該列的數據將被跳過。

維度列有所不同,因爲它們支持篩選和分組操作,因此每個維度都需要以下三個數據結構:

  1. 一個將列值(始終被視爲字符串)映射到整數ID的字典
  2. 一個使用1中的字典編碼的列值列表
  3. 對於列中的每個不同值,一個位圖指示哪些行包含該值。

爲什麼要使用這三個數據結構?字典僅將字符串值映射爲整數id,以便可以緊湊地表示(2)和(3)中的值。(3)中的位圖-也稱爲倒排索引,允許快速過濾操作(特別是,位圖便於快速應用AND和OR運算符)。最後,group byTopN 查詢需要(2)中的值列表。換句話說,僅基於過濾器彙總指標的查詢不需要使用到存儲在(2)中的維度值列表(注:因爲不需要根據維度進行分組)。

爲了具體瞭解這些數據結構,請考慮上面示例數據中的“ page”列。下圖說明了表示該維度的三個數據結構。

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]

請注意,位圖與前兩個數據結構不同:前兩個在數據大小上呈線性增長(在最壞的情況下),而位圖部分的大小則是數據大小*列基數的乘積。壓縮對於位圖十分有用,因爲我們知道對於“列數據”中的每一行,只有一個位圖的條目爲非零。這意味着高基數列將具有極爲稀疏、高度可壓縮位圖。德魯伊使用特別適合位圖的壓縮算法(例如咆哮的位圖壓縮)來利用這一點。

Multi-value columns 多值列

如果數據源使用多值列,則段文件中的數據結構看起來會有所不同。讓我們想象一下,在上面的示例中,第二行同時標記了“ Ke $ ha”  “ Justin Bieber”主題。在這種情況下,這三個數據結構現在看起來如下:

1: Dictionary that encodes column values
  {
    "Justin Bieber": 0,
    "Ke$ha":         1
  }

2: Column data
  [0,
   [0,1],  <--多值列的行值擁有數組形式的值
   1,
   1]

3: Bitmaps - one for each unique value
  value="Justin Bieber": [1,1,0,0]
  value="Ke$ha":         [0,1,1,1]
                            ^
                            |
                            |
    Multi-value column has multiple non-zero entries
 

注意列數據和Ke $ ha位圖中第二行的更改。如果一行的一個列有多個值,則其在“列數據”中的輸入是一組值。此外, “列數據”中具有值的行在位圖中將具有n個非零值條目。

SQL兼容的空處理

默認情況下,Druid字符串維列的null值 將會被替換爲'',而數值和度量列完全不可能出現null,因爲null會強制轉換爲0。Druid還提供了與SQL兼容的null處理模式,但是需要在系統級別啓用該模式,將druid.generic.useDefaultValueForNull設置爲false,Druid在攝取時間創建的段,其字符串列可以區分''null,並且數字列可以存儲null而不是0

字符串維度列在此模式下不包含任何其他列結構,而是僅保留該null值的其他字典條目。但是,數值維度列將與附加位圖一起存儲在段中,該位圖的設置位顯示有null值的列。除了需要稍微增加段大小外,由於需要檢查空位圖,因此與SQL兼容的空處理也可能在查詢時產生性能成本。該性能開銷僅發生在實際上包含空值的列上。

命名約定

段標識符通常使用段數據源、間隔開始時間(ISO 8601格式)、間隔結束時間(ISO 8601格式)和版本來構造。如果另外將數據分片超出時間範圍,則段標識符還將包含分區號。

一個示例段標識符可以是:datasource_intervalStart_intervalEnd_version_partitionNum

段組件

在幕後,一個段由幾個文件組成,下面列出。

  • version.bin

    4個字節,以整數表示當前段版本。例如,對於v9段,版本爲0x0、0x0、0x0、0x9

  • meta.smoosh

    具有有關其他smoosh文件內容的元數據(文件名和偏移量)的文件

  • XXXXX.smoosh

    這些文件中有一些是串聯的二進制數據

    這些smoosh文件代表在一起“ smooshed”的多個文件,以減少必須打開以容納數據的文件描述符的數量。它們是最大2GB的文件(以匹配Java中內存映射的ByteBuffer的限制)。這些smoosh文件包含數據中每個列的單獨文件,以及index.drd帶有有關該段的額外元數據的文件。

    還有一個稱爲__time的特殊列,它表示段的時間列。希望隨着代碼的發展,這種特殊性越來越小,但就目前而言,它就像我媽媽一直告訴我的那樣特殊。

在代碼庫中,段具有內部格式版本。當前的句段格式版本爲v9

列格式

每列存儲爲兩部分:

  1. Jackson序列化的ColumnDescriptor
  2. 該列的其餘二進制文件

ColumnDescriptor本質上是一個對象,它使我們能夠使用Jackson的多態反序列化來添加新的有趣的序列化方法,而對代碼的影響最小。它由一些有關該列的元數據組成(它是什麼類型,它是多值的,等等),然後是可以反序列化二進制其餘部分的序列化/反序列化邏輯列表。

分片數據以創建細分

分片

對於同一數據源,在相同的時間間隔內可能存在多個段。這些段形成一個block間隔。根據shardSpec用於分片數據的類型,僅當a block完成時,Druid查詢纔可能完成。也就是說,如果一個塊由3個段組成,例如:

sampleData_2011-01-01T02:00:00:00Z_2011-01-01T03:00:00:00Z_v1_0

sampleData_2011-01-01T02:00:00:00Z_2011-01-01T03:00:00:00Z_v1_1

sampleData_2011-01-01T02:00:00:00Z_2011-01-01T03:00:00:00Z_v1_2

在查詢間隔2011-01-01T02:00:00:00Z_2011-01-01T03:00:00:00Z完成之前,必須裝入所有3個段。

該規則的例外是使用線性分片規範。線性分片規範不會強制“完整性”,即使分片未加載到系統中,查詢也可以完成。例如,如果您的實時攝取創建了3個使用線性分片規範進行分片的段,並且系統中僅加載了兩個段,則查詢將僅返回這2個段的結果。

模式變更

Replacing segments 替換段

Druid使用數據源、時間間隔、版本和分區號唯一地標識段。如果在一段時間內創建了多個段,則分區號僅在段ID中可見。例如,如果您有按小時分的段,但一個小時內的數據量超過單個段所能容納的數據,則可以在同一小時內創建多個段。這些段將共享相同的數據源,間隔和版本,但分區號線性增加。

foo_2015-01-01/2015-01-02_v1_0
foo_2015-01-01/2015-01-02_v1_1
foo_2015-01-01/2015-01-02_v1_2
 

另外,在上述中,實例中的片段dataSource = foointerval = 2015-01-01/2015-01-02version = v1,和partitionNum = 0。如果在以後的某個時間點,您使用新的架構重新索引數據,則新創建的段將具有更高的版本ID。

foo_2015-01-01/2015-01-02_v2_0
foo_2015-01-01/2015-01-02_v2_1
foo_2015-01-01/2015-01-02_v2_2
 

Druid批處理索引(基於Hadoop或基於IndexTask)可確保每個間隔的原子更新。在我們的示例中,在將所有v22015-01-01/2015-01-02都加載到Druid集羣中之前,查詢僅使用v1段。一旦v2加載了所有段並可以查詢,所有查詢將忽略v1段並切換到這些v2段。過段時間後,v1段將會從集羣之中卸載

請注意,跨越多個段間隔的更新僅是每個間隔內的原子。在整個更新過程中,它們不是原子的。例如,您具有如下段:

foo_2015-01-01/2015-01-02_v1_0
foo_2015-01-02/2015-01-03_v1_1
foo_2015-01-03/2015-01-04_v1_2
 

v2分段一旦構建,就會被加載到集羣中,並v1在分段重疊的時間段內替換分段。在完全加載v2段之前,您的羣集可能包含v1v2段的混合。

foo_2015-01-01/2015-01-02_v1_0
foo_2015-01-02/2015-01-03_v2_1
foo_2015-01-03/2015-01-04_v1_2

在這種情況下,查詢可能會混合使用v1v2指標。

段之間的模式不同

同一數據源的Druid段可能具有不同的架構。如果一個段中存在一個字符串列(維度),但另一個段中不存在,則涉及這兩個段的查詢仍然有效。缺少維的段查詢將表現得好像維只有空值。同樣,如果一個段中有一個數字列(指標),而另一部分則沒有,則對缺少指標的段查詢通常會“做正確的事”。此缺失指標上的聚合的行爲就像該指標缺失一樣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章