ReplacingMergeTree 테이블 엔진

ReplacingMergeTree 테이블 엔진

이 엔진은 같은 정렬 키 값(PRIMARY KEY가 아닌 ORDER BY 테이블 섹션)을 가진 중복 항목을 제거한다는 점에서 MergeTree와 달라요.

데이터 중복 제거는 병합 중에만 발생해요. 병합은 알 수 없는 시점에 백그라운드에서 발생하므로 예측할 수 없어요. 일부 데이터는 처리되지 않은 채 남을 수 있어요. OPTIMIZE 쿼리로 예정되지 않은 병합을 실행할 수는 있지만, 그것에 의존하지 마세요. OPTIMIZE 쿼리는 많은 양의 데이터를 읽고 쓰기 때문이에요.

따라서 ReplacingMergeTree는 공간을 절약하기 위해 백그라운드에서 중복 데이터를 정리하는 데 적합하지만, 중복이 없다는 것을 보장하지는 않아요. ReplacingMergeTree에 대한 자세한 안내(모범 사례와 성능 최적화 방법 포함)는 여기에서 확인할 수 있어요.

출처: 문서

본문

테이블 생성하기

CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
    name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1],
    name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2],
    ...
) ENGINE = ReplacingMergeTree([ver [, is_deleted]])
[PARTITION BY expr]
[ORDER BY expr]
[PRIMARY KEY expr]
[SAMPLE BY expr]
[SETTINGS name=value, ...]

요청 매개변수에 대한 설명은 statement description을 참고해요. 행의 고유성은 PRIMARY KEY가 아닌 ORDER BY 테이블 섹션에 의해 결정돼요.

ReplacingMergeTree 매개변수

ver

ver — 버전 번호가 있는 컬럼. 타입 UInt*, Date, DateTime 또는 DateTime64. 선택 매개변수.

병합할 때 ReplacingMergeTree는 같은 정렬 키를 가진 모든 행에서 하나만 남겨요.

  • ver가 설정되지 않았으면 선택(selection)에서 마지막 것. 선택은 병합에 참여하는 파트 집합의 행 집합이에요. 가장 최근에 생성된 파트(마지막 삽입)가 선택에서 마지막이 돼요. 따라서 중복 제거 후 각 고유 정렬 키에 대해 가장 최근 삽입의 맨 마지막 행이 남아요
  • ver가 지정되면 최대 버전을 가진 것. 여러 행의 ver가 같다면 "ver가 지정되지 않은" 규칙이 적용돼요. 즉 가장 최근에 삽입된 행이 남아요

예시:

-- without ver - the last inserted 'wins'
CREATE TABLE myFirstReplacingMT
(
    `key` Int64,
    `someCol` String,
    `eventTime` DateTime
)
ENGINE = ReplacingMergeTree
ORDER BY key;

INSERT INTO myFirstReplacingMT Values (1, 'first', '2020-01-01 01:01:01');
INSERT INTO myFirstReplacingMT Values (1, 'second', '2020-01-01 00:00:00');

SELECT * FROM myFirstReplacingMT FINAL;

┌─key─┬─someCol─┬───────────eventTime─┐
│   1 │ second  │ 2020-01-01 00:00:00 │
└─────┴─────────┴─────────────────────┘

-- with ver - the row with the biggest ver 'wins'
CREATE TABLE mySecondReplacingMT
(
    `key` Int64,
    `someCol` String,
    `eventTime` DateTime
)
ENGINE = ReplacingMergeTree(eventTime)
ORDER BY key;

INSERT INTO mySecondReplacingMT Values (1, 'first', '2020-01-01 01:01:01');
INSERT INTO mySecondReplacingMT Values (1, 'second', '2020-01-01 00:00:00');

SELECT * FROM mySecondReplacingMT FINAL;

┌─key─┬─someCol─┬───────────eventTime─┐
│   1 │ first   │ 2020-01-01 01:01:01 │
└─────┴─────────┴─────────────────────┘

is_deleted

is_deleted — 병합 중 이 행의 데이터가 상태를 나타내는지 삭제되어야 하는지 결정하는 데 사용되는 컬럼 이름; 1은 "deleted" 행, 0은 "state" 행. 컬럼 데이터 타입 — UInt8.

is_deletedver를 사용할 때만 활성화할 수 있어요. 데이터에 대한 어떤 연산을 하든 버전은 증가해야 해요. 두 삽입된 행이 같은 버전 번호를 가지면 마지막으로 삽입된 행이 유지돼요.

기본적으로 ClickHouse는 행이 delete 행이더라도 키에 대한 마지막 행을 유지해요. 이는 더 낮은 버전의 향후 행이 안전하게 삽입될 수 있고 delete 행이 여전히 적용되도록 하기 위해서예요.

이러한 delete 행을 영구적으로 제거하려면 테이블 설정 allow_experimental_replacing_merge_with_cleanup을 활성화하고 다음 중 하나를 수행해요.

  • 테이블 설정 enable_replacing_merge_with_cleanup_for_min_age_to_force_merge, min_age_to_force_merge_on_partition_only, min_age_to_force_merge_seconds를 설정해요. 파티션의 모든 파트가 min_age_to_force_merge_seconds보다 오래되면 ClickHouse가 그것들을 모두 단일 파트로 병합하고 delete 행을 제거해요
  • 수동으로 OPTIMIZE TABLE table [PARTITION partition | PARTITION ID 'partition_id'] FINAL CLEANUP을 실행해요

예시:

-- with ver and is_deleted
CREATE OR REPLACE TABLE myThirdReplacingMT
(
    `key` Int64,
    `someCol` String,
    `eventTime` DateTime,
    `is_deleted` UInt8
)
ENGINE = ReplacingMergeTree(eventTime, is_deleted)
ORDER BY key
SETTINGS allow_experimental_replacing_merge_with_cleanup = 1;

INSERT INTO myThirdReplacingMT Values (1, 'first', '2020-01-01 01:01:01', 0);
INSERT INTO myThirdReplacingMT Values (1, 'first', '2020-01-01 01:01:01', 1);

select * from myThirdReplacingMT final;

0 rows in set. Elapsed: 0.003 sec.

-- delete rows with is_deleted
OPTIMIZE TABLE myThirdReplacingMT FINAL CLEANUP;

INSERT INTO myThirdReplacingMT Values (1, 'first', '2020-01-01 00:00:00', 0);

select * from myThirdReplacingMT final;

┌─key─┬─someCol─┬───────────eventTime─┬─is_deleted─┐
│   1 │ first   │ 2020-01-01 00:00:00 │          0 │
└─────┴─────────┴─────────────────────┴────────────┘

쿼리 절

ReplacingMergeTree 테이블을 만들 때 MergeTree 테이블을 만들 때와 같은 절들이 필요해요.

쿼리 시간 중복 제거 & FINAL

병합 시간에 ReplacingMergeTree는 ORDER BY 컬럼(테이블을 만들 때 사용)의 값을 고유 식별자로 사용해 중복 행을 식별하고 최고 버전만 유지해요. 그러나 이것은 궁극적(final) 정확성만 제공해요 — 행이 중복 제거될 것이라고 보장하지는 않으며 그것에 의존해서는 안 돼요. 따라서 쿼리는 업데이트와 삭제 행이 쿼리에서 고려되므로 잘못된 답을 만들 수 있어요.

올바른 답을 얻으려면 사용자가 백그라운드 병합을 쿼리 시간 중복 제거와 삭제 제거로 보완해야 해요. 이는 FINAL 연산자로 달성할 수 있어요. 예를 들어 다음 예시를 고려해요.

CREATE TABLE rmt_example
(
    `number` UInt16
)
ENGINE = ReplacingMergeTree
ORDER BY number

INSERT INTO rmt_example SELECT floor(randUniform(0, 100)) AS number
FROM numbers(1000000000)

0 rows in set. Elapsed: 19.958 sec. Processed 1.00 billion rows, 8.00 GB (50.11 million rows/s., 400.84 MB/s.)

FINAL 없이 조회하면 잘못된 개수가 생성돼요 (정확한 결과는 병합에 따라 달라져요):

SELECT count()
FROM rmt_example

┌─count()─┐
│     200 │
└─────────┘

1 row in set. Elapsed: 0.002 sec.

final을 추가하면 올바른 결과가 나와요:

SELECT count()
FROM rmt_example
FINAL

┌─count()─┐
│     100 │
└─────────┘

1 row in set. Elapsed: 0.002 sec.

FINAL에 대한 자세한 내용(FINAL 성능 최적화 방법 포함)은 ReplacingMergeTree에 대한 상세 안내를 읽는 것을 권장해요.

더 알아보기 (Learn more)