UPDATE 문
UPDATE 문
경량 업데이트(lightweight update)는 현재 베타 상태예요. 문제가 발생하면 ClickHouse 저장소에 이슈를 열어 주세요. 경량 UPDATE 문은 filter_expr 표현식과 일치하는 [db.]table 테이블의 행을 업데이트해요. 데이터 파트의 전체 열을 다시 쓰는 무거운 프로세스인 ALTER TABLE ... UPDATE 쿼리와 대비되어 “경량 업데이트”라고 불러요. 이는 MergeTree 테이블 엔진 계열에서만 사용할 수 있어요.
출처: 문서
본문
경량 업데이트는 현재 베타 상태예요. 문제가 발생하면 ClickHouse repository에 이슈를 열어 주세요.
경량 UPDATE 문은 filter_expr 표현식과 일치하는 [db.]table 테이블의 행을 업데이트해요. 데이터 파트의 전체 열을 다시 쓰는 무거운 프로세스인 ALTER TABLE ... UPDATE 쿼리와 대비되어 “경량 업데이트”라고 불러요. 이는 MergeTree 테이블 엔진 계열에서만 사용할 수 있어요.
UPDATE [db.]table [ON CLUSTER cluster] SET column1 = expr1 [, ...] [IN PARTITION partition_expr1 [, partition_expr2 ...]] WHERE filter_expr;
filter_expr은 UInt8 타입이어야 해요. 이 쿼리는 filter_expr이 0이 아닌 값을 가지는 행에서 지정된 열의 값을 해당 표현식의 값으로 업데이트해요. 값은 CAST 연산자를 사용해 열 타입으로 캐스팅돼요. 기본 키나 파티션 키 계산에 사용되는 열을 업데이트하는 것은 지원되지 않아요. IN PARTITION 절은 업데이트를 나열된 파티션으로 제한해요. 이 절이 없으면 ReplicatedMergeTree 계열 테이블에서 optimize_mutations_with_partition_pruning 설정(기본값)이 활성화될 때 ClickHouse가 filter_expr에서 파티션 키 조건을 자동으로 감지하고 영향을 받는 파티션만 업데이트해요. 비복제 MergeTree 테이블에서는 명시적인 IN PARTITION 절을 사용해 업데이트를 특정 파티션으로 제한해요.
Examples (예제)
UPDATE hits SET Title = 'Updated Title' WHERE EventDate = today();
UPDATE wikistat SET hits = hits + 1, time = now() WHERE path = 'ClickHouse';
Lightweight updates are immediately visible in queries (경량 업데이트는 쿼리에서 즉시 보임)
경량 UPDATE는 patch parts를 적용함으로써 업데이트된 값을 SELECT 쿼리에서 즉시 보이게 해줘요. 쿼리는 업데이트된 값을 읽기 위해 merge를 기다릴 필요가 없어요. Patch parts는 업데이트된 열과 행만 포함하는 특별한 종류의 데이터 파트예요. 이들을 만든다고 해서 원본 데이터가 스토리지에서 물리적으로 즉시 수정되지는 않아요. 업데이트 프로세스는 INSERT ... SELECT ... 쿼리와 유사하며, UPDATE 쿼리는 patch part 생성이 완료될 때까지 기다렸다가 반환돼요. 쿼리 가시성과 물리적 구체화(materialization)는 별개예요.
- Query visibility (쿼리 가시성):
SELECT쿼리는 patch가 원본 데이터 파트로 구체화되기 전에 업데이트된 값을 읽기 위해 patch를 적용해요. - Physical materialization (물리적 구체화): 이후의 merges와 mutations은 업데이트된 값을 데이터 파트에 통합해요.
- Cleanup (정리): 모든 활성 파트에 patch가 구체화되면 patch parts는 자동으로 제거돼요.
동시 업데이트의 일관성에 대해서는 Concurrent operations를 참고해요.
Lightweight updates requirements (경량 업데이트 요구 사항)
경량 업데이트는 MergeTree, ReplacingMergeTree, CollapsingMergeTree, VersionedCollapsingMergeTree 엔진과 그 Replicated 및 Shared 버전에서 지원돼요. 경량 업데이트를 사용하려면 _block_number 및 _block_offset 열의 구체화가 테이블 설정 enable_block_number_column과 enable_block_offset_column으로 활성화되어 있어야 해요.
Lightweight deletes (경량 삭제)
경량 DELETE 쿼리는 ALTER UPDATE mutation 대신 경량 UPDATE로 실행될 수 있어요. 경량 DELETE의 구현은 lightweight_delete_mode 설정으로 제어돼요.
Performance considerations (성능 고려 사항)
경량 업데이트의 장점:
- 업데이트의 지연 시간은
INSERT ... SELECT ...쿼리의 지연 시간과 비슷해요. - 데이터 파트의 전체 열이 아니라 업데이트된 열과 값만 기록돼요.
- 현재 실행 중인 merges/mutations이 완료되기를 기다릴 필요가 없으므로 업데이트의 지연 시간이 예측 가능해요.
- 경량 업데이트의 병렬 실행이 가능해요.
잠재적인 성능 영향:
- patch를 적용해야 하는
SELECT쿼리에 오버헤드를 추가해요. - Skipping indexes는 적용할 patch가 있는 데이터 파트의 열에 대해 사용되지 않아요. 테이블에 patch parts가 있으면, 적용할 patch가 없는 데이터 파트를 포함해 Projections이 사용되지 않아요.
- 너무 잦은 작은 업데이트는 “too many parts” 오류로 이어질 수 있어요. 여러 업데이트를 단일 쿼리로 배치하는 것이 권장돼요. 예를 들어 업데이트할 id들을
WHERE절의 단일IN절에 넣는 식으로요. - 경량 업데이트는 적은 양의 행(테이블의 약 10%까지)을 업데이트하도록 설계됐어요. 더 많은 양을 업데이트해야 한다면 ALTER TABLE ... UPDATE mutation을 사용하는 것이 권장돼요.
Concurrent operations (동시 작업)
경량 업데이트는 무거운 mutation과 달리 현재 실행 중인 merges/mutations이 완료되기를 기다리지 않아요. 동시 경량 업데이트의 일관성은 update_sequential_consistency 및 update_parallel_mode 설정으로 제어돼요.
Update permissions (업데이트 권한)
UPDATE에는 ALTER UPDATE 권한이 필요해요. 특정 사용자에 대해 특정 테이블에서 UPDATE 문을 활성화하려면 다음을 실행해요.
GRANT ALTER UPDATE ON db.table TO username;
Details of the implementation (구현 세부 사항)
Patch parts는 일반 파트와 같지만 업데이트된 열과 몇 가지 시스템 열만 포함해요.
_part- 원본 파트의 이름_part_offset- 원본 파트의 행 번호_block_number- 원본 파트에서 행의 블록 번호_block_offset- 원본 파트에서 행의 블록 오프셋_data_version- 업데이트된 데이터의 데이터 버전(UPDATE쿼리에 할당된 블록 번호)
평균적으로 patch parts에서 업데이트된 행당 약 40바이트(압축되지 않은 데이터)의 오버헤드가 생겨요. 시스템 열은 업데이트해야 할 원본 파트의 행을 찾는 데 도움을 줘요. 시스템 열은 원본 파트의 virtual columns과 관련되며, patch parts를 적용해야 할 때 읽기를 위해 추가돼요. Patch parts는 _part와 _part_offset로 정렬돼요. Patch parts는 원본 파트와 다른 파티션에 속해요. patch part의 파티션 id는 patch-<hash of column names in patch part>-<original_partition_id>예요. 따라서 열이 다른 patch parts는 서로 다른 파티션에 저장돼요. 예를 들어 SET x = 1 WHERE <cond>, SET y = 1 WHERE <cond>, SET x = 1, y = 1 WHERE <cond> 세 업데이트는 서로 다른 세 파티션에 세 개의 patch parts를 생성해요. Patch parts는 SELECT 쿼리에 적용되는 patch 양을 줄이고 오버헤드를 줄이기 위해 서로 병합될 수 있어요. Patch parts의 병합은 _data_version을 버전 열로 사용하는 replacing 병합 알고리즘을 사용해요. 따라서 patch parts는 파트의 각 업데이트된 행에 대한 최신 버전을 항상 저장해요. 경량 업데이트는 현재 실행 중인 merges와 mutations이 끝나기를 기다리지 않고 항상 데이터 파트의 현재 스냅샷을 사용해 업데이트를 실행하고 patch part를 생성해요. 그 때문에 patch parts 적용에는 두 가지 경우가 있을 수 있어요. 예를 들어 파트 A를 읽는다면 patch part X를 적용해야 해요.
X가 파트A자체를 포함하는 경우.UPDATE실행 시A가 merge에 참여하지 않았을 때 발생해요.X가 파트A가 커버하는 파트B와C를 포함하는 경우.UPDATE실행 시 merge (B,C) ->A가 실행 중이었을 때 발생해요.
이 두 경우에 대해 각각 두 가지 방식으로 patch parts를 적용할 수 있어요.
- 정렬된 열
_part,_part_offset로 병합(merge) 사용. _block_number,_block_offset열로 조인(join) 사용.
조인 모드는 병합 모드보다 느리고 더 많은 메모리가 필요하지만, 덜 자주 사용돼요.
더 알아보기 (Learn more)
- ALTER UPDATE - 무거운
UPDATE작업 - Lightweight DELETE - 경량
DELETE작업 - APPLY PATCHES - 데이터 파트에 patch의 물리적 구체화 강제 (mutation 작업)