CTAS 로 대량 적재하는 중 tablet server 가 쓰기를 거부하기 시작하는 상황을 추적하고, 메모리 구성 요소별 해석과 한도 조정 기준을 정리한다.
W Status: Remote error: Service unavailable: soft memory limit exceeded (82%)
call kudu... write from <ip>
soft limit 을 넘으면 tserver 는 쓰기 요청을 Service unavailable 로 거부하고, Kudu 클라이언트는 백오프 후 재시도한다. 쓰기가 들어오면 tserver 는 우선 메모리의 MemRowSet 에 쌓고 백그라운드 maintenance 스레드가 디스크로 flush 하는데, 순간 유입이 flush 속도를 앞지르면 메모리가 계속 차오른다.
curl -s http://<tserver>:8050/mem-trackers | head -50
curl -s http://<tserver>:8050/memz | head -30
curl -s http://<tserver>:8050/maintenance-manager
curl -s http://<tserver>:8050/metrics | grep -i block_cache
mem-trackers 의 항목별 소비량에서 무엇이 큰지 가른다. 태블릿별 MemRowSet 합이 크면 유입이 flush 를 앞지르는 중이고, block_cache 가 크면 캐시 설정 문제이며, log_cache 가 크면 복제 지연을 의심한다.
숫자를 읽을 때 두 가지를 구분해야 한다. soft limit 판정은 프로세스 전체 사용량(Total consumption) 기준인데, 트래커에 잡히는 root 값은 그보다 작을 수 있다. 그 차이는 대부분 tcmalloc 이 해제된 메모리를 OS 에 반납하지 않고 들고 있는 free-list 와 힙 단편화다. 대량 쓰기가 몰렸다 빠지면 MemRowSet 이 비워져도 Total 은 높게 유지된다. memz 의 Bytes in page heap freelist 가 그 양을 보여 준다.
log_cache 는 WAL 엔트리를 팔로워 복제용으로 들고 있는 캐시다. 전역 한도가 기본 1GB 수준임을 감안하면 수 GB 는 크다. 복제가 지연되는 태블릿이 있으면 커지므로 ksck 로 확인한다.
kudu cluster ksck <master-addresses>
tcmalloc 이 쥔 미반납분은 웹 UI 엔드포인트로 OS 에 돌려줄 수 있다. 버전에 따라 경로가 다르므로 memz 출력의 안내를 함께 본다.
curl -X POST "http://<tserver>:8050/mem-trackers?release_memory=1"
실사용 중인 MemRowSet 은 회수 대상이 아니라 flush 로 내려야 하는 부분이고, maintenance 스레드가 정상 동작 중이면 여기서 짜낼 여지는 크지 않다. log_cache 는 pin 이 풀리면 자연 감소하지만 유휴 상태에서도 높게 머물면 수동으로 털 방법이 없어, 이 부분이 재시작이 필요한 실질적 이유가 된다.
| 설정 | 의미 | 조정 기준 |
|---|---|---|
memory_limit_hard_bytes |
tserver 메모리 상한 | 예약이 아니라 상한. 호스트 물리 RAM 을 넘기면 OOM Killer 나 스와핑으로 이어진다 |
maintenance_manager_num_threads |
flush · compaction 스레드 수 | 데이터 디스크 수에 맞춰 조정 |
flush_threshold_mb |
MemRowSet flush 임계 | 유입 패턴에 따라 점검 |
block_cache_capacity_mb |
읽기 블록 캐시 | 읽기 성능용. hard limit 안에서 함께 계산된다 |
hard limit 은 Kudu 전용 노드면 물리 RAM 의 70~80% 까지 주고, Impala daemon 이나 DataNode 가 같은 호스트에 있으면 그 사용량과 OS·페이지 캐시 여유를 뺀 값으로 잡는다. 적용에는 tserver 재시작이 필요하다.
free -g
block cache 는 대량 INSERT 적재에는 도움이 되지 않는다. 쓰기 경로(MemRowSet → flush → WAL)와 캐시 경로가 다르고, 캐시가 차지한 메모리는 hard limit 안에서 함께 계산되므로 쓰기에 쓸 메모리를 잠식한다. 증설이 효과를 내는 것은 반복 조회가 많거나 PK 기반 랜덤 조회가 많을 때이며, block_cache_hits_caching 과 block_cache_misses_caching 비율에서 히트율이 90% 아래로 떨어질 때 부족을 의심한다. 95% 이상이면 늘려도 얻을 것이 없다.
block cache 는 데이터 블록뿐 아니라 PK 인덱스와 블룸 필터 같은 메타데이터 블록도 캐싱한다. 인덱스 블록이 캐시에 없으면 행 하나를 찾는 데도 디스크 I/O 가 여러 번 발생하므로, UPSERT · UPDATE · DELETE 처럼 PK 존재 여부를 먼저 확인하는 연산에도 간접적으로 영향을 준다.
hard limit 을 올려도 한 번에 다 밀어 넣으면 flush 가 따라오지 못해 같은 스로틀이 생긴다. 테이블 단위나 범위 단위로 끊어 넣고 사이에 여유를 준다.
INSERT INTO target SELECT ... WHERE <범위>
적재 중에는 각 tserver 의 memz 를 주기적으로 보고, 사용량이 새 soft limit(hard limit 의 82%)에 접근하면 적재를 잠시 멈춰 flush 가 따라잡을 시간을 준다. WAL 디스크 여유도 미리 확인한다.
tserver 를 재시작하면 그 서버가 가진 태블릿이 WAL 재생(bootstrap)을 거쳐야 RUNNING 이 된다. 태블릿이 많고 직전에 대량 쓰기가 있었으면 수 분에서 수십 분이 걸리며, 그동안 ksck 는 다수를 unhealthy 로 보고한다. 대부분 정상 복구 과정이다.
curl -s http://<tserver>:8050/tablets \
| grep -oE "BOOTSTRAPPING|RUNNING|FAILED|NOT_STARTED" | sort | uniq -c
BOOTSTRAPPING 이 점차 RUNNING 으로 옮겨 가면 기다리는 것이 조치다. 5분 간격으로 비율이 늘고 있는지만 확인한다. FAILED 가 다수면 tserver 로그에서 디스크 오류나 WAL 손상 같은 실패 사유를 확인해야 한다. 30분이 지나도 수치가 전혀 줄지 않으면 그때가 비정상이다.
ksck 가 깨끗한 것을 확인한 뒤 다음 대로 넘어간다.INVALIDATE METADATA <table> 을 한다.