INSERT 와 DELETE 를 반복한 테이블이 실제 데이터양보다 훨씬 많은 디스크를 쓴다. RDBMS 의 REORG 나 VACUUM 에 해당하는 명령을 찾지만 Kudu 에는 없다.
Kudu 는 태블릿마다 쓰기를 먼저 메모리의 MemRowSet 에 쌓고, 일정 조건에서 디스크의 DiskRowSet 으로 flush 한다. 이후의 UPDATE·DELETE 는 기존 DiskRowSet 을 다시 쓰지 않고 델타 파일(REDO/UNDO)로 따로 기록한다. 그래서 변경이 많은 테이블일수록 베이스 데이터보다 델타가 커지고, 조회할 때 베이스와 델타를 합쳐 읽어야 하므로 스캔도 느려진다.
백그라운드 maintenance 스레드가 이를 정리한다. 델타를 베이스에 합치는 델타 컴팩션(minor/major)과, 키 범위가 겹치는 DiskRowSet 을 병합하는 rowset 컴팩션이 있다. 두 작업 모두 사용자가 직접 실행하는 명령이 없고, maintenance 관리자가 이득이 큰 순서로 골라 수행한다.
DELETE 한 공간은 컴팩션이 끝나야 회수된다. 삭제 직후에 du 가 줄지 않는 것은 정상이다.
먼저 복제본 배수를 뺀다. 기본 복제 계수가 3이므로 논리 데이터 400GB 면 클러스터 전체로 1.2TB 를 쓰는 것이 정상이다. 한 대에서 본 수치인지 클러스터 합계인지부터 구분한다.
kudu table statistics <master-addresses> <table-name>
kudu cluster ksck <master-addresses>
그 다음 컴팩션이 실제로 돌고 있는지 본다.
curl -s http://<tserver>:8050/maintenance-manager
curl -s http://<tserver>:8050/tablets
maintenance-manager 에 대기 중인 작업은 많은데 실행 스레드가 항상 포화 상태면 스레드 수가 모자란 것이고, 대기 작업 자체가 없는데 델타가 크면 컴팩션 임계값이 높게 잡혀 있는 것이다.
| 플래그 | 기본값 | 효과 |
|---|---|---|
--maintenance_manager_num_threads |
1 | flush·compaction 을 동시에 수행할 스레드 수. 데이터 디스크 수에 맞춰 올린다 |
--maintenance_manager_polling_interval_ms |
250 | 다음에 할 작업을 고르는 주기 |
--flush_threshold_mb |
1024 | MemRowSet 을 flush 하는 크기 임계 |
--flush_threshold_secs |
120 | 크기에 못 미쳐도 이 시간이 지나면 flush |
--delta_store_major_compact_min_ratio |
0.1 | 베이스 대비 델타 비율이 이 값을 넘으면 major 델타 컴팩션 후보 |
--tablet_delta_store_minor_compact_max |
1000 | minor 델타 컴팩션을 유발하는 델타 파일 수 |
--tablet_compaction_budget_mb |
128 | 한 번의 rowset 컴팩션이 쓸 수 있는 메모리 예산 |
모두 tablet server 플래그이고 대부분 재시작이 필요하다. 런타임 변경이 가능한 것은 kudu tserver set_flag 로 확인한 뒤 적용한다.
kudu tserver get_flags <tserver>:7050 | grep -E "flush_threshold|maintenance_manager|delta_store"
--flush_threshold_secs 를 기본 120초에서 크게 늘리면(예: 3600초) flush 횟수가 줄어 작은 파일이 덜 생기는 대신, 메모리에 머무는 데이터가 많아져 WAL GC 가 밀리고 재시작 시 WAL 재생 시간이 길어진다. 쓰기가 꾸준한 클러스터에서는 기본값 근처를 유지하는 편이 낫다.
컴팩션을 직접 호출하는 명령은 없으므로, 즉시 회수가 필요하면 테이블을 다시 쓰는 방법밖에 없다.
CREATE TABLE db.tbl_new PRIMARY KEY (...) PARTITION BY ... STORED AS KUDU
AS SELECT * FROM db.tbl;
새 테이블은 한 번에 적재되므로 델타 없이 깔끔한 상태로 만들어진다. 다만 적재 중 원본과 동일한 크기의 공간이 추가로 필요하고, 적재 부하도 그만큼 든다.
--tablet_split_threshold_mb 같은 자동 분할 설정은 존재하지 않는다.