청크(Chunk) 이해하기

청크(Chunk) 이해하기

하이퍼테이블이 빠른 이유를 들여다보면, 그 바탕에 **청크(chunk)**라는 물리적 파티션이 있어요. 각 청크는 특정 시간 범위(그리고 필요하면 공간 키)의 행들을 담는 테이블이고, 쿼리할 때는 항상 하이퍼테이블 이름을 쓰면 엔진이 알아서 올바른 청크들로 작업을 분배해요. 보통은 청크를 직접 다룰 일이 거의 없지만, 스키마를 설계하고 성능을 튜닝할 때 이 구조를 이해해 두면 훨씬 수월해져요.

출처: 공식문서 - Understand chunks

청크란 무엇인가요?

청크는 하이퍼테이블의 수평 파티션이에요. 특정 시간 범위(여러 차원을 쓰면 특정 공간 파티션까지)의 행을 담는 테이블이죠. 하이퍼테이블에 데이터를 넣을 때 TimescaleDB는 모든 것을 하나의 거대한 테이블에 저장하지 않고, 각자 일정 시간(예: 하루, 일주일)을 담당하는 작은 테이블들로 나눠요. 이 분할은 자동으로 그리고 투명하게 일어나므로, 나는 항상 하이퍼테이블 이름으로 INSERT·쿼리하고 시스템이 뒤에서 청크를 만들고 사용해요.

쉽게 말하면 하이퍼테이블은 SQL로 보이는 논리적 테이블이고, 청크는 데이터를 실제로 저장하는 물리적 파티션이에요. 하이퍼테이블을 하나의 테이블로, 청크를 "쿼리나 쓰기가 필요할 때만 열리는 내부 서랍"으로 생각하면 돼요.

참고: 하이퍼테이블(따라서 청크)에 테이블 상속(inheritance)은 지원하지 않아요. 하이퍼테이블에 상속을 쓰면 예상치 못한 동작이 생길 수 있어요.

청크가 중요한 이유

청크는 단순한 구현 세부사항이 아니에요. 바로 청크 덕분에 하이퍼테이블이 SQL 작성 방식을 바꾸지 않고도 효율적인 데이터 관리·더 나은 쿼리 성능·확장 가능한 유지보수를 제공할 수 있어요.

쿼리 성능

시계열 쿼리는 보통 시간으로 필터링해요(예: "지난 24시간", "2024년 9월"). 각 청크가 알려진 시간 범위를 담기 때문에, 쿼리 플래너는 해당 범위에 데이터가 없는 청크를 건너뛸 수 있어요. 그래서 전체 테이블을 스캔하는 대신 조건에 맞는 행이 있을 수 있는 청크만 건드려요.

유지보수와 데이터 수명주기

시간으로 파티셔닝하면 시계열 DB의 많은 작업이 더 쉬워져요.

  • 컬럼 저장소(columnstore) 전환: 정책이 오래된 청크는 컬럼 저장소로, 최근 청크는 행 저장소로 유지하게 할 수 있어요. 컬럼 저장소 청크는 압축 해제 없이도 INSERT·UPDATE·DELETE를 직접 지원해요. 변환은 청크 단위로 적용돼요.
  • 데이터 보존(retention): 오래된 데이터를 행별 DELETE로 지우는 대신 drop_chunks()로 청크 전체를 버리면 블로트(bloat)를 피하고 훨씬 빨라요.
  • 계층형 저장(tiered storage): 클라우드에서는 청크를 나이·접근 패턴에 따라 다른 저장 계층으로 옮길 수 있어요.

즉, 청크는 시스템에 "이 시간 블록은 핫(hot)이다, 이 블록은 콜드(cold)다, 이 블록은 컬럼 저장소로 바꾸거나 버릴 수 있다"는 명확한 경계를 만들어 줘서, 자동화와 튜닝이 예측 가능해져요.

병렬성과 인덱싱

청크는 독립적인 저장 단위예요. 쿼리를 실행할 때 DB가 여러 청크를 병렬로 처리할 수 있고, 인덱스도 청크별로 유지돼요. 덕분에 인덱스 크기를 관리 가능하게 유지하고 플래너가 청크 단위 통계를 쓸 수 있어요. 하이퍼테이블을 만들면 기본적으로 시간 내림차순 인덱스가 생기고, 내가 추가하는 인덱스도 청크 레벨로 만들어져서 행이 많을 때 INSERT·쿼리 성능을 높여 줘요.

청크와 청크 간격의 관계

**청크 간격(chunk interval)**은 각 청크가 담당하는 시간 범위예요(예: 하루, 7일). 하이퍼테이블을 만들 때(또는 나중에 set_chunk_time_interval()로) 설정하며, 주어진 시간 범위에서 청크가 몇 개 생기는지 결정해요. 간격이 짧을수록 더 많고 작은 청크가, 길수록 더 적고 큰 청크가 생겨요.

  • 기본값: 대개 각 청크가 약 7일 치 데이터를 담지만, 수집·쿼리 방식에 맞게 바꿀 수 있어요(고부하·시간 범위 쿼리에 하루 1청크 등).
  • 생성: 시간이 아직 청크가 없는 범위에 해당하는 행을 넣으면 그 범위용 새 청크를 자동으로 만들어요. 새 시간 범위에 데이터가 들어올수록 청크 수가 늘어나는 거죠.
  • 컬럼 저장소 전환: 컬럼 저장소 정책이 특정 나이(예: 청크 간격 하나 경과) 이후에 청크를 컬럼 형식으로 바꾸는 경우가 많아서, 청크 간격이 데이터가 컬럼 저장소로 언제 이동하는지에도 영향을 줘요.

올바른 청크 간격을 고르는 건 트레이드오프예요. 활성(최근) 청크와 그 인덱스가 메모리에 들어가 빠르게 접근할 만큼 충분히 작게, 하지만 쿼리 플래닝을 느리게 하거나 컬럼 저장소 효율을 낮출 만큼 청크가 너무 많아지지 않게 잡아야 해요.

청크를 효과적으로 쓰는 방법

정상적인 사용에서는 청크를 직접 만들거나 관리할 필요가 없어요. 하이퍼테이블을 만들고 INSERT·쿼리하면 청크는 자동으로 생성·사용돼요. "청크를 효과적으로 쓴다"는 것은 청크 분할이 유리하게 작동하도록 설계·튜닝한다는 뜻이에요.

  1. 적절한 청크 간격 고르기: 하이퍼테이블 생성 시 청크 간격을 설정해요. 흔한 기준은 현재 수집 중인 청크들의 인덱스가 주 메모리의 합리적 비율(예: RAM의 25%)을 차지하도록 간격을 잡는 거예요.
  2. 쿼리에 시간 기반 필터 사용하기: 파티션(시간) 컬럼으로 필터링하는 쿼리는 플래너가 무관한 청크를 건너뛰게 해요.
  3. 튜닝·디버깅 시 청크 확인하기: show_chunks()로 하이퍼테이블의 청크를 나열하고, chunks_detailed_size()로 크기를 확인할 수 있어요. 청크 간격을 바꿀지, 쿼리 플랜을 조사할 때 유용해요.
  4. 벌크 수명주기 작업은 청크 단위로: 오래된 데이터를 지울 때는 큰 DELETE 대신 보존 정책이나 drop_chunks()를 쓰세요. 컬럼 저장소 전환도 청크 단위로 이뤄지므로 청크 간격과 정책 설정이 함께 작동해요.

더 알아보기 (Learn more)