CodeGym /행동 /SQL SELF /JSONB 데이터 인덱싱: GIN과 BTREE 인덱스 사용하기

JSONB 데이터 인덱싱: GIN과 BTREE 인덱스 사용하기

SQL SELF
레벨 34, 레슨 1
사용 가능

PostgreSQL에서 인덱싱은 데이터베이스에서 데이터를 빠르게 찾는 방법이야. 만약 네 테이블에 있는 데이터가 책이라면, 인덱싱은 도서관의 카탈로그 같은 거지. 제목이나 저자 이름으로 원하는 책을 금방 찾을 수 있게 해주는 거야. 근데 JSONB는 좀 더 까다로워. 왜냐면 데이터가 구조화된 포맷으로 저장돼 있어서, 그냥 행(row)이나 컬럼(column)처럼 단순하지 않거든.

JSONB 데이터가 "해리포터 책만큼 두꺼워지면, 근데 그림은 없는 버전"처럼 커지면, 그 안에서 뭔가 찾는 게 진짜 느려질 수 있어. 예를 들어, 특정 키 "status"가 "delivered"인 모든 주문을 찾고 싶으면, PostgreSQL이 모든 레코드를 다 뒤져야 해. 이거 직접 하라고 하면 진짜 귀찮겠지?

그래서 GIN이랑 BTREE 인덱스가 우리의 구세주처럼 등장하는 거야! 이 친구들이 있으면 오래 기다릴 필요가 없어.

JSONB용 인덱스 타입

GIN (Generalized Inverted Index)

GIN 인덱스는 배열이나 오브젝트 같은 구조화된 데이터에 딱 맞게 만들어진 거야. 그래서 JSONB에 완전 잘 맞아. 전체 오브젝트를 인덱싱하는 게 아니라, 그 안에 있는 각각의 키와 값들을 따로따로 인덱싱해줘. 즉, GIN 덕분에 특정 키, 값, 혹은 그 조합을 빠르게 찾을 수 있어.

예를 들어, JSONB 컬럼에 이런 데이터가 있다고 해보자:

{"name": "Alice", "age": 25, "city": "Berlin"}

GIN 인덱스는 내부적으로 "name", "age", "city" 키랑 그 값들을 연결해서 저장해. 그래서 "name": "Alice"를 찾으려고 하면, PostgreSQL이 이미 어디에 있는지 알고 있어서 테이블 전체를 뒤지지 않아도 돼.

BTREE

BTREE 인덱스는 좀 더 전통적인 방식이야. 정렬된 구조를 만들어서, 특정 값으로 데이터를 빠르게 찾을 수 있게 해줘. JSONB에서 BTREE 인덱스를 쓰려면, 전체 오브젝트가 아니라 특정 키나 값을 인덱싱하는 게 좋아 (예를 들어, JSONB 전체 값이 똑같은지 비교할 때).

만약 네 컬럼에 이런 JSONB 오브젝트가 있다면:

{"name": "Bob", "age": 30}

BTREE 인덱스는 전체 오브젝트가 완전히 똑같은 레코드를 찾을 때 유용해.

{"name": "Bob", "age": 30}

JSONB 인덱스 만들기

먼저 GIN 인덱스 만드는 방법부터 보자. 필요한 건 CREATE INDEX라는 마법의 명령어뿐이야. 이렇게 쓰면 돼:

-- JSONB 컬럼에 GIN 인덱스 만들기
CREATE INDEX idx_jsonb_data ON orders USING GIN (data);

여기서:

  • idx_jsonb_data — 인덱스 이름이야.
  • orders — 테이블 이름.
  • data — JSONB 데이터가 들어있는 컬럼.

이 인덱스를 만들고 나면, JSONB 안에서 키나 값을 찾는 쿼리가 훨씬 빨라질 거야.

예를 들어, orders라는 테이블에 data라는 JSONB 컬럼이 있다고 해보자:

id data
1 {"status": "pending", "total": 100}
2 {"status": "delivered", "total": 200}

인덱스 없이 쿼리하면:

-- "delivered" 상태인 모든 주문 찾기
SELECT * FROM orders WHERE data @> '{"status": "delivered"}';

테이블이 크면 이 쿼리가 엄청 오래 걸릴 수 있어. 근데 GIN 인덱스가 있으면 훨씬 빨라져.

BTREE 인덱스 만드는 법

BTREE 인덱스를 만들려면 살짝 다르게 해야 해. 대부분의 경우, JSONB에서 BTREE를 쓰려면 전체 오브젝트가 아니라 그 안의 일부만 인덱싱해야 해. 예시를 볼게:

-- 특정 키에 BTREE 인덱스 만들기
CREATE INDEX idx_jsonb_total ON orders ((data->>'total'));

여기서 (data->>'total')은 JSONB 오브젝트에서 total 키의 값을 뽑아내는 거야. 이 값을 인덱싱하는 거지. 이제 total = 100인 주문을 찾으면 PostgreSQL이 이 인덱스를 써줄 거야.

아까랑 똑같은 데이터로 예시를 들어볼게:

id data
1 {"status": "pending", "total": 100}
2 {"status": "delivered", "total": 200}

쿼리 예시:

-- total = 100인 모든 주문 찾기
SELECT * FROM orders WHERE data->>'total' = '100';

data->>'total'에 BTREE 인덱스가 있으면, 이 쿼리도 훨씬 빨라질 거야.

GIN vs BTREE 비교

특징 GIN BTREE
뭐가 인덱싱됨? JSONB 안의 키와 값 지정한 경로나 값
최적의 사용 시나리오 오브젝트 일부 검색 특정 값 검색
인덱스 생성 속도 느림 빠름
검색 속도 복잡한 구조에 더 빠름 고정된 값에 더 빠름
지원 연산자 @>, ?, `? ,?&`

만약 네 JSONB 구조가 복잡하고 @>나 ? 같은 연산자를 자주 쓴다면 GIN을 써. 반대로, 특정 값이나 고정된 키만 자주 찾는다면 BTREE가 더 나을 수도 있어.

JSONB 인덱싱할 때 흔히 하는 실수와 함정

JSONB 인덱싱은 강력하지만, 몇 가지 조심해야 할 점이 있어.

  1. 필요한 곳에 인덱스가 없음. JSONB 데이터를 WHERE에서 자주 쓰는데 인덱스를 안 만들면, 쿼리가 엄청 느려질 수 있어.
  2. 인덱스를 너무 많이 만듦. JSONB의 모든 키마다 인덱스를 만들면, 오히려 insert나 update가 느려질 수 있어.
  3. 인덱스 타입을 잘못 선택함. 쿼리가 복잡하고 @>나 ? 같은 연산자를 쓰는데 BTREE 인덱스를 만들면, 성능 향상이 안 돼.
  4. 경로(path)에 대한 이해 부족. 항상 중첩된 값을 찾는데, 그 경로에 인덱스를 안 만들면 쿼리가 여전히 느릴 거야 (예: data->>'some_key').

정리: 언제 어떤 인덱스를 써야 할까?

  • GIN은 배열이나 복잡한 오브젝트, 키/값 검색이 많을 때 써.
  • BTREE는 정확히 일치하는 값이나 특정 키만 자주 찾을 때 써.
2
과제
SQL SELF, 레벨 34, 레슨 1
잠금
JSONB 데이터에서 검색을 빠르게 하기 위한 `GIN` 인덱스 생성
JSONB 데이터에서 검색을 빠르게 하기 위한 `GIN` 인덱스 생성
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION