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_stats의 correlation 값으로 확인할 수 있는데, 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