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가지 데이터 구조로 저장된다.
- int형의 id와 value가 매칭된 dictionary
- 위 dictionary를 통해 인코딩된 column값이 저장된 list
- 각 column의 값에 대해 어떤 row가 해당 값을 갖고 있는지 나타내는 bitmap
- dimension value를 위와 같은 데이터 구조에 저장함으로써 다음과 같은 이점이 있다.
- id와 실제값이 연결된 dictionary를 통해 2, 3번의 list와 bitmap을 id로 압축하여 표현할 수 있다.
- list로 저장한 데이터를 통해 group by와 topN 쿼리를 수행한다.
- 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
반응형