본문 바로가기
Database2026년 8월 12일5분 읽기

BRIN 인덱스 — 대용량 시계열에 맞는 초경량 인덱스

YS
김영삼
조회 8
BRIN 인덱스 — 대용량 시계열에 맞는 초경량 인덱스

BRIN(Block Range Index)은 테이블의 물리적 블록 범위마다 그 안에 든 값의 최소·최대만 요약해 저장하는 초경량 인덱스다. PostgreSQL이 지원하며, 값이 저장 순서와 자연스럽게 정렬되는 대용량 데이터, 특히 시간순으로 쌓이는 로그·시계열 테이블에서 B-트리의 수십분의 일 크기로 비슷한 범위 검색 효과를 낸다.

처음 BRIN을 봤을 때 "이렇게 작은 인덱스가 정말 쓸모가 있나" 싶었다. 수억 행짜리 로그 테이블에서 B-트리 인덱스가 수 기가바이트인데 BRIN은 수십 메가바이트로 끝난다. 조건만 맞으면 이 대비가 그냥 공짜 점심처럼 느껴진다.

B-트리와 근본이 다르다

B-트리는 모든 행의 위치를 정교하게 담는다. 그래서 정확하고 만능이지만, 행이 많을수록 인덱스도 그만큼 커진다. BRIN은 정반대 발상이다. 행 하나하나가 아니라 연속된 블록 묶음(기본 128페이지)마다 요약값 하나만 저장한다.

검색할 때는 이 요약을 보고 "이 블록 범위엔 찾는 값이 있을 수도 있다 / 절대 없다"를 판단한다. 없는 구간은 통째로 건너뛰고, 있을 법한 구간만 실제로 훑는다. 정밀 조준이 아니라 넓은 영역을 빠르게 쳐내는 방식이다.

-- 시간순으로 쌓이는 로그 테이블
CREATE INDEX idx_logs_created_brin
  ON logs USING BRIN (created_at);
-- 범위 조회에서 관련 블록만 스캔
SELECT * FROM logs
WHERE created_at >= '2026-08-01'
  AND created_at <  '2026-08-02';

전제 조건: 물리적 정렬 상관성

여기가 BRIN의 생명줄이다. 인덱스 컬럼의 값이 테이블에 저장된 물리적 순서와 상관관계가 높아야 한다. 시계열은 보통 시간순으로 append되니 created_at이 자연스럽게 정렬돼 쌓인다. 이럴 때 각 블록 범위의 min/max가 좁고 겹치지 않아, 건너뛰기가 잘 먹힌다.

반대로 값이 여기저기 흩어져 저장돼 있으면? 블록마다 min/max 범위가 넓게 겹쳐서, 결국 대부분의 블록을 다 확인하게 된다. 그러면 BRIN은 무용지물이다. 나는 이 상관성을 모르고 랜덤한 UUID 컬럼에 BRIN을 걸었다가 전혀 안 먹혀서 한참 갸웃했다. 상관성은 pg_statscorrelation 값으로 확인할 수 있는데, 1이나 -1에 가까울수록 좋다.

B-트리 vs BRIN

항목B-트리BRIN
인덱스 크기큼(행 수 비례)아주 작음
단일 행 조회빠름비효율적
넓은 범위 조회좋음좋음(크기 대비 탁월)
정렬 상관성 요구불필요필수
유지 비용높음매우 낮음

튜닝과 함정

  • pages_per_range요약 단위를 조절한다. 기본 128인데, 낮추면 인덱스가 더 정밀해지고 커지며, 높이면 더 작아지고 거칠어진다. 데이터 밀도를 보며 실측으로 맞춘다.
  • 새 데이터가 계속 append되면 마지막 범위 요약이 갱신돼야 정확해진다. 대량 적재 후엔 brin_summarize_new_values()를 호출하거나 autovacuum이 정리하도록 둔다.
  • 정확한 값 하나를 콕 집어 찾는 조회(WHERE id = 12345)에는 부적합하다. BRIN은 범위를 좁혀줄 뿐, 최종적으로 해당 블록 안은 실제로 다 훑어야 한다.
  • UPDATE·DELETE가 잦아 행이 뒤섞이면 상관성이 무너진다. BRIN은 거의 추가만 일어나는 테이블에 어울린다.

정리하면 BRIN은 좁은 틈새를 정확히 겨냥한 인덱스다. "수억 행, 시간순 적재, 범위 조회 위주, 인덱스 용량이 아까운 상황" — 이 조건이 겹치면 BRIN만큼 가성비 좋은 선택이 없다. 반대로 조건이 안 맞으면 있으나 마나다. 만능이 아니라 딱 맞는 곳에 꽂아 넣는 도구라는 감각으로 접근하면 배신당할 일이 없다.

자주 묻는 질문

BRIN을 아무 컬럼에나 걸어도 되나요?

안 됩니다. 컬럼 값이 테이블의 물리적 저장 순서와 상관성이 높아야 효과가 납니다. 시간순으로 쌓이는 created_at, 순차 증가 ID 등이 적합하고, 랜덤 UUID처럼 흩어진 값에는 사실상 쓸모가 없습니다.

BRIN 인덱스가 실제로 잘 동작하는지 어떻게 확인하나요?

EXPLAIN (ANALYZE)Bitmap Index Scan이 잡히고 실제로 읽은 블록 수가 크게 줄었는지 봅니다. 또 pg_stats의 correlation 값이 1 또는 -1에 가까운지 확인하면 적합성을 미리 가늠할 수 있습니다.

파티셔닝과 BRIN 중 무엇을 써야 하나요?

둘은 경쟁이 아니라 보완 관계입니다. 파티셔닝으로 큰 덩어리를 나누고, 각 파티션 안에서 BRIN으로 범위를 좁히는 조합이 흔합니다. 다만 파티션 프루닝만으로 충분하다면 BRIN이 없어도 되는 경우도 있습니다.

BRIN도 다중 컬럼을 지원하나요?

지원합니다. 여러 컬럼에 대해 각각 min/max 요약을 만듭니다. 다만 각 컬럼이 개별적으로 정렬 상관성을 가져야 의미가 있어서, 실무에서는 단일 컬럼(주로 시간)에 거는 경우가 대부분입니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록