Storage/Druid

druid segment

Prower 2022. 7. 26. 23:31
728x90
반응형

# 이 글은 druid segment 를 참고하여 정리한 글입니다.

  • druid는 index 정보를 시간에 따라 파티셔닝된 segment 파일에 저장한다.
    • 여기서 파티셔닝이 되는 시간 단위는 segmentGranularity 값을 통해 설정 가능한다.
  • 보통 druid가 무거운 쿼리를 적절한 성능으로 처리하기 위해서 segment 파일은 300 MB ~ 700 MB 사이를 유지할 것을 권장한다.
    • 만약 해당 segment의 용량이 해당 값 범위 밖에 있다면 segmentGranularity 값을 조정해야 한다.

segment 파일의 구조

  • 컬럼 지향(columnar): druid의 특징으로 각 column의 데이터가 분리되어 저장되었음을 것을 의미한다.
  • column 데이터를 분리하여 저장함으로써, 필요한 column만 조하도록 하여 쿼리 실행시간을 단축시킬 수 있다.

ref: https://druid.apache.org/docs/latest/design/segments.html

  • 위 그림처럼 각 column이 나눠 저장되있는 구조를 생각해 볼 수 있다.
  • 위 그림에서도 확인할 수 있듯이, column의 기본 종류로 timestamp, dimension, metric column이 있다.

timestamp, metric

  • timestamp와 metric은 기본적으로 int와 float 값의 배열로 이루어져 있으며, LZ4 방식으로 압축된 상태로 저장되어 있다.
  • 쿼리 요청이 들어오면 압축을 풀어 관련된 row를 추출하고, 집계 연산을 수행한다.
  • 만약 timestamp와 metric을 조회하는 쿼리가 아니면 위 과정은 건너뛴다.

dimension

  • dimension은 filter와 group by가 적용될 수 있기 때문에 dimension의 데이터는 다음의 3가지 데이터 구조로 저장된다.
    1. int형의 id와 value가 매칭된 dictionary
    2. 위 dictionary를 통해 인코딩된 column값이 저장된 list
    3. 각 column의 값에 대해 어떤 row가 해당 값을 갖고 있는지 나타내는 bitmap
  • dimension value를 위와 같은 데이터 구조에 저장함으로써 다음과 같은 이점이 있다.
    1. id와 실제값이 연결된 dictionary를 통해 2, 3번의 list와 bitmap을 id로 압축하여 표현할 수 있다.
    2. list로 저장한 데이터를 통해 group by와 topN 쿼리를 수행한다.
    3. bitmap으로 저장한 데이터를 사용하여 filter 연산을 빠르게 수행 가능(특히 and, or 조건에 대해 좋은 퍼포먼스를 제공한다)

데이터가 저장된 예시

1: dictionary that encodes column values
  {
    "python":   0,
    "java":     1,
    "nodejs":   2
  }

2: list of column data
  [0, 0, 2, 1, 1, 2]

3: bitmaps
  value="python":   [1,1,0,0,0,0]
  value="java":     [0,0,0,1,1,0]
  value="nodejs":   [0,0,2,0,0,2]

multi value column

  • mulit-value column이 저장되어 있다면 구조가 조금 다르다.

multi value가 저장된 예시

1: dictionary that encodes column values
  {
    "python":   0,
    "java":     1,
    "nodejs":   2
  }

2: list of column data
  [0, 0, [1, 2], 1, 1, 2]

3: bitmaps
  value="python":   [1,1,0,0,0,0]
  value="java":     [0,0,1,1,1,0]
  value="nodejs":   [0,0,2,0,0,2]
  • 위 케이스에서 column의 세번째 값으로 multi value가 저장되어 있다.
  • 해당 값을 list에서 배열로 저장되고, bitmap에도 반영된다.

null handling

  • dimension 은 빈 string '' 과 null 값은 서로 교환 가능한 값으로 취급된다.
  • numeric 값이나 metric 은 null 을 표현할 수 없으며 대신 null 을 0 으로 표현한다.

null handling mode

  • 시스템 레벨에서 useDefaultValueForNull 값을 조정하여 null handling mode를 설정할 수 있다.
  • false: 데이터 적재 시점에 '' 과 null 을 구분하여 저장하며, numeric column도 0 대신 null 로 표기된다.

sharding

  • 같은 datasoure 내에서 같은 시간단위 안에 여러개의 segment가 존재할 수 있다.
  • 이러한 segment를 모아 해당 시간단위의 block을 구성할 수 있다.
  • shardSpec의 종류에 따라서 block의 구성이 완료돼야 druid 쿼리의 실행이 완료될 수 있다.

예시

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
  • 위와 같은 3개의 segment가 있다고 한다.
  • 3개의 segment는 2011-01-01T02:00:00:00Z_2011-01-01T03:00:00:00Z 에 대한 조회 쿼리가 끝나기 전에 로드가 완료 되어야 한다.
  • 즉 3개 segment로 block이 구성되어야 쿼리 실행이 완료될 수 있음

segment 교체

  • druid는 datasource, interval, version, partition number를 사용하여 segment에 id값을 부여한다.
  • 같은 interval안에 여러 segment가 있다면 partition number는 고유한 값으로 부여된다.

예시

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
  • 예를들어 위와 같이 day 단위의 interval 안에 여러개의 segment가 존재한다.
  • 여기서 맨 뒤의 partition number를 증가시키며 segment를 생성하여 고유한 id를 부여할 수 있다.

version을 통한 갱신

  • 위에서 각 segment는 id값에 version이 포함됨을 알 수 있다.
  • version 값을 사용하여 druid 쿼리 결과가 atomic하게 갱신되는 것을 보장할 수 있다.
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
  • 위와 같은 v2 데이터가 새로 추가될 때 모든 v2 데이터가 로드되기 전 까지 druid는 v1 버전의 데이터만 쿼리 결과로 반환한다.
  • v2 데이터가 모두 로드되면 v1 데이터는 v2로 교체된 뒤 더이상 쿼리되지 않으며 이후 cluster에서 제외된다.
728x90
반응형