Skip to Content
BlogClickHouse replica 장애 — 승격 대신 무엇을 확인하나

ClickHouse replica 장애 — 승격 대신 무엇을 확인하나

#ClickHouse#복제#Keeper#트러블슈팅

PostgreSQL 장애 대응에 익숙하면 replica가 죽었을 때 “누구를 primary로 승격할까”부터 떠올리기 쉽습니다.

ReplicatedMergeTree에서는 질문이 다릅니다.

살아 있는 replica는 이미 쓰기를 받을 수 있고 복제 로그를 각자 따라갑니다.

따라서 장애 때 확인할 것은 승격 대상이 아니라 쓰기 주소, Keeper 연결, 미처리 복제 작업입니다.

is_leader는 쓰기 primary가 아니다

system.replicas.is_leader는 머지와 mutation 같은 작업을 배정하는 역할을 보여줍니다.

클라이언트가 쓰기를 보내야 할 단일 primary를 뜻하지 않습니다.

ClickHouse는 과거 하나의 replica가 leader 역할을 양보하던 구조에서 여러 replica가 동시에 leader가 될 수 있는 구조로 바뀌었습니다.

그 이력은 ClickHouse issue #10367 , PR #11639 , PR #11795 에서 확인할 수 있습니다.

모든 replica가 is_leader=0이면 쓰기 primary가 없어진 것이 아니라 머지 스케줄링이 멈춘 조정 이상을 의심해야 합니다.

저사양 replica를 머지 할당에서 제외하는 replicated_can_become_leader도 같은 맥락의 설정입니다.

복제는 pull로 진행된다

새 파트 정보는 Keeper의 공용 복제 로그에 등록됩니다.

각 replica는 그 로그를 자기 queue로 가져오고 필요한 파트를 다른 replica에서 fetch하거나 같은 입력으로 머지를 실행합니다.

새 엔트리 pull순서대로 실행GET_PARTMERGE_PARTSMUTATE_PARTKeeper /log공용 복제 로그replica queuesystem.replication_queue타입별 실행다른 replica에서part fetch로컬 mergemutation 적용
공용 복제 로그를 각 replica가 자기 queue로 가져와 실행한다

잠깐 내려갔던 replica는 돌아온 뒤 자기 log_pointer 다음부터 다시 따라갑니다.

다른 노드가 그 replica로 모든 파트를 밀어 넣는 구조가 아닙니다.

이 차이 때문에 장애 복구는 프로세스를 띄우는 일과 queue가 비워지는 일을 나눠 봐야 합니다.

첫 쿼리는 상태 요약이다

SELECT database, table, replica_name, is_leader, is_readonly, absolute_delay, queue_size, inserts_in_queue, merges_in_queue, log_pointer, log_max_index, log_max_index - log_pointer AS log_behind, total_replicas, active_replicas FROM system.replicas ORDER BY database, table, replica_name;

컬럼은 묶어서 읽어야 합니다.

신호해석
is_readonly=1Keeper 세션이나 메타데이터 경로 문제로 쓰기 불가
absolute_delay 증가replica의 데이터 시각이 뒤처짐
log_behind 증가공용 로그에서 아직 가져오지 못한 범위 증가
queue_size 증가가져왔지만 실행하지 못한 작업 증가
active_replicas < total_replicas현재 연결 가능한 사본 감소

queue_size=0 하나만으로 정상 판정을 내리면 안 됩니다.

Keeper에서 새 로그를 읽지 못해 queue 자체가 늘지 않는 경우에는 log_behindis_readonly가 더 직접적인 신호입니다.

막힌 작업은 타입과 마지막 오류로 본다

SELECT database, table, replica_name, type, source_replica, new_part_name, num_tries, last_attempt_time, last_exception FROM system.replication_queue WHERE num_tries > 0 ORDER BY last_attempt_time DESC LIMIT 100;

자주 보는 type은 다음과 같습니다.

type하는 일
GET_PART다른 replica에서 part 받기
MERGE_PARTS같은 입력 part를 로컬에서 병합
MUTATE_PARTmutation 적용
DROP_RANGE / REPLACE_RANGE파티션 범위 변경
ATTACH_PARTpart 연결
ALTER_METADATA스키마 메타데이터 반영

num_tries가 큰 항목의 last_exception부터 읽으면 네트워크, 없는 source part, 디스크 부족을 빠르게 가를 수 있습니다.

복구 명령 네 개는 서로 대체재가 아니다

SYSTEM SYNC REPLICA db.table; SYSTEM RESTART REPLICA db.table; SYSTEM RESTORE REPLICA db.table; SYSTEM DROP REPLICA 'replica_name' FROM TABLE db.table;
명령사용할 때주의
SYNC REPLICA현재 queue 처리를 기다려 완료 판정원인을 고치지 않음
RESTART REPLICAKeeper 상태와 queue를 다시 초기화데이터 복원 명령이 아님
RESTORE REPLICAKeeper 메타데이터를 잃었고 로컬 part로 등록 복원선행 조건과 테이블 상태 확인 필수
DROP REPLICA영구 폐기한 replica의 Keeper 등록 정리살아 있는 replica 이름에 실행 금지

복구 명령을 순서대로 모두 실행하는 런북은 위험합니다.

증상과 데이터 위치를 확인한 뒤 필요한 명령 하나를 골라야 합니다.

특히 DROP REPLICA는 대상 이름이 현재 동작 중인 replica가 아닌지 다른 노드에서 먼저 확인해야 합니다.

Keeper 과반을 잃으면 주소 변경으로 해결되지 않는다

Keeper 세 대 중 두 대를 잃으면 데이터 replica가 모두 살아 있어도 복제 테이블은 read-only가 될 수 있습니다.

기존 로컬 part를 읽는 SELECT는 계속될 수 있지만, 신규 part 등록이 필요한 INSERT와 DDL, 복제 조정은 진행할 수 없습니다.

조정 로그 기록쓰기 거부네트워크 분할Keeper 과반에 연결Keeper 과반 없음기존 read + 새write기존 local readwrite는 read-only로 거부
네트워크가 갈리면 Keeper 과반에 닿는 쪽만 새 쓰기를 조정할 수 있다

소수파의 쓰기를 막는 것은 장애가 아니라 split-brain을 피하는 보호 동작입니다.

이 상태에서는 Service가 다른 ClickHouse Pod를 가리키게 해도 그 Pod 역시 Keeper 과반에 닿지 못한다면 쓰기는 살아나지 않습니다.

Service readiness는 쓰기 가능 여부까지 봐야 한다

Kubernetes Service는 readiness에 실패한 Pod를 새 연결의 endpoint에서 뺍니다.

하지만 /ping만 성공한다고 복제 테이블에 쓸 수 있다는 뜻은 아닙니다.

프로세스는 살아 있고 SELECT도 되지만 is_readonly=1일 수 있습니다.

쓰기 endpoint의 readiness나 별도 헬스 체크는 다음 조건을 구분해야 합니다.

프로세스 생존 Keeper 연결 가능 대상 ReplicatedMergeTree가 read-write replication lag가 허용 범위

native TCP 9000 연결을 일반 L4 프록시가 쿼리 단위로 다른 replica에 나눠 주는 것도 아닙니다.

한 번 열린 연결은 한 backend에 붙어 있으므로 드라이버의 재접속과 host failover 설정을 확인해야 합니다.

RF2 정비는 단일 사본 운영 시간이다

RF2에서 한 replica를 정비하면 남은 한 벌이 읽기와 쓰기를 담당합니다.

그동안 두 번째 장애가 나면 PDB가 구해주지 못합니다.

PDB는 계획된 eviction을 제한할 뿐 하드웨어 고장을 막지 않습니다.

따라서 정비 완료 조건은 Pod Running이 아닙니다.

active_replicas가 목표 RF로 복귀 log_behind가 0 또는 허용 범위 queue_size와 inserts_in_queue 정상화 absolute_delay 정상화 실제 INSERT와 SELECT 성공

이 다섯 조건이 채워진 뒤 다음 replica를 건드려야 합니다.

장애 때의 확인 순서

  1. 클라이언트의 새 연결이 살아 있는 replica로 향하는지 봅니다.
  2. system.replicas에서 is_readonly, active 수, log와 queue 지연을 확인합니다.
  3. Keeper 과반과 ClickHouse의 Keeper 세션을 확인합니다.
  4. system.replication_queue의 반복 오류를 원인별로 분류합니다.
  5. 로컬 볼륨이 남았는지, 새 볼륨에 재수화해야 하는지 구분합니다.
  6. 필요한 복구 명령만 실행합니다.
  7. queue와 지연이 정상화된 뒤 서비스 endpoint에 복귀시킵니다.

결론 : ReplicatedMergeTree 장애 대응의 중심은 primary 승격이 아니라 Keeper에 닿는 쓰기 경로와 각 replica의 pull queue입니다.

동작과 링크 상태는 2026-09-15 스냅샷입니다.