CodeGym /행동 /SQL SELF /JSONB 타입 데이터 인덱싱: GIN...

JSONB 타입 데이터 인덱싱: GINBTREE 인덱스 사용하기

SQL SELF
레벨 38 , 레슨 0
사용 가능

첫 번째 질문: 도대체 왜 JSONB를 쓰는 걸까? JSONB는 JSON 포맷으로 데이터를 저장할 수 있게 해주고, 구조가 엄청 유연해. 데이터가 복잡하거나 중첩된 구조(예: 여러 주소나 설정이 들어있는 사용자 프로필 등)를 가질 때 특히 편하지. 그냥 JSON이랑 다르게, JSONB는 데이터를 바이너리 포맷으로 저장해서 검색이나 필터링 작업이 훨씬 빨라져.

근데 인덱스 없이 JSONB에서 검색하면 진짜 느릴 수 있어, 특히 테이블에 수천, 수백만 개의 행이 있으면 더더욱. 예를 들어, 사용자 정보를 담은 테이블이 있고, 각 사용자의 설정을 JSONB로 저장한다고 해보자. 이 설정에서 특정 값을 가진 사용자를 찾으려면 인덱스 없이는 리소스를 엄청 잡아먹어. 이럴 때 인덱스가 진짜 빛을 발하지!

JSONB 인덱싱: 중요한 포인트

PostgreSQL에서 JSONB를 인덱싱하는 방법은 크게 두 가지야:

  1. GIN (Generalized Inverted Index)JSONB 내부의 키와 값 검색용.
  2. BTREE — 간단한 검색이나 정렬용.

각각 장단점이 있어. 하나씩 자세히 보자.

JSONBGIN 인덱스

GIN은 배열, 텍스트, 그리고 JSONB 데이터에 잘 맞는 강력한 인덱스야. JSONB 오브젝트의 내용을 키와 값으로 쪼개서, 빠른 검색을 위한 특별한 구조를 만들어줘.

JSONBGIN을 쓰는 장점:

  • 키뿐만 아니라 값으로도 검색 가능.
  • 중첩 구조도 잘 지원함.
  • @>, ?, ?|, ?& 같은 연산자(키/값 필터링)에서 속도가 확 올라감.

예를 들어, users라는 테이블이 있고, settings 컬럼에 JSONB로 사용자 설정을 저장한다고 해보자. 데이터 예시는 이래:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name TEXT,
    settings JSONB
);

INSERT INTO users (name, settings) VALUES
('Alice', '{"theme": "dark", "notifications": {"email": true, "sms": false}}'),
('Bob', '{"theme": "light", "notifications": {"email": false, "sms": true}}'),
('Charlie', '{"theme": "dark", "notifications": {"email": true, "sms": true}}');

이제 다크 테마(theme: dark)를 쓰는 모든 사용자를 빠르게 찾고 싶다고 해보자. 먼저 인덱스를 만들어:

CREATE INDEX idx_users_settings_gin ON users USING GIN (settings);

그리고 @> 연산자로 검색해봐:

SELECT name
FROM users
WHERE settings @> '{"theme": "dark"}';

이제 PostgreSQL이 GIN 인덱스를 써서 훨씬 빠르게 검색해줄 거야.

이게 어떻게 동작하냐면, JSONB 컬럼에 GIN 인덱스를 만들면 PostgreSQL이 "역방향" 인덱스를 만들어. 즉, JSON의 모든 키와 값에 대해 별도의 인덱스 레코드를 만들어주는 거지. 예를 들어, 이런 오브젝트가 있으면:

{"theme": "dark", "notifications": {"email": true, "sms": false}}

theme, notifications.email, notifications.sms 같은 키와 그 값들로 인덱싱해. 그래서 각각의 요소로 빠르게 검색할 수 있어.

JSONBBTREE 인덱스

BTREE는 전통적인 인덱스 방식이야. JSONB 오브젝트 전체를 비교하거나 정렬할 때 써. 근데 GIN이랑 다르게, BTREE는 JSON 오브젝트 내용을 쪼개서 보진 않아.

JSONBBTREE를 쓰는 장점:

  • 오브젝트 전체 비교나 정렬에 딱 좋아.
  • JSONB를 "덩어리"로 쓸 때(예: 다른 오브젝트랑 비교하거나, JSONB가 특정 값이랑 같은 행을 찾을 때) 더 빠름.

BTREE 인덱스 사용 예시를 들어볼게. 예를 들어, users 테이블에서 settings 컬럼을 특정 오브젝트랑 자주 비교한다고 해보자:

{"theme": "dark", "notifications": {"email": true, "sms": false}}

먼저 인덱스를 만들어:

CREATE INDEX idx_users_settings_btree ON users USING BTREE (settings);

이제 오브젝트 전체 비교 쿼리를 날릴 수 있어:

SELECT name
FROM users
WHERE settings = '{"theme": "dark", "notifications": {"email": true, "sms": false}}';

이 쿼리는 BTREE 인덱스를 써서 빨라질 거야.

GIN vs BTREE 비교

특징 GIN BTREE
JSONB 오브젝트 쪼개기 응, 키와 값으로 쪼갬 아니, 전체 비교
중첩 구조 검색 아니
정렬 아니
인덱스 크기 더 큼 더 작음
지원 연산자 @>, ?, ?|, ?& =, >, < 등

즉, GIN은 복잡한 쿼리에, BTREE는 오브젝트 전체 비교나 정렬에 더 잘 맞아.

어떤 인덱스를 써야 할까?

  • JSONB 내부의 키나 값으로 검색하고 싶으면 GIN을 써.
  • JSONB 오브젝트 전체를 비교하거나 정렬해야 하면 BTREE가 더 좋아.

근데 꼭 하나만 써야 하는 건 아니야! 테이블에서 두 가지 쿼리가 다 필요하면 GIN이랑 BTREE 인덱스를 둘 다 만들어도 돼.

JSONB 인덱싱할 때 자주 하는 실수

쓸데없는 인덱스 만들기: JSONB의 모든 필드를 인덱싱하는 건 별로야. 인덱스는 공간도 차지하고, 데이터 삽입/업데이트도 느려질 수 있어.

잘 안 쓰는 연산자 인덱싱: 그냥 "왠지 인덱스가 있어야 할 것 같아서" 만들지 말고, 실제 쿼리를 분석해서 진짜 필요한 데만 인덱싱해.

GIN 특징 무시하기: GIN 인덱스는 BTREE보다 생성 시간이 더 오래 걸릴 수 있어. 큰 테이블에 인덱싱할 땐 이 점 꼭 고려해야 해.

실전에서의 활용

JSONB는 데이터가 유연하고 자주 바뀌는 실제 프로젝트에서 진짜 유용해. 예를 들면:

  • 사용자 설정이 다양한 웹앱.
  • 이벤트마다 필드가 다른 로그 저장.
  • JSON 포맷으로 캐시 데이터 저장.

이런 데이터를 GIN이나 BTREE로 인덱싱하면 쿼리 성능이 확 좋아져. 예를 들어, 면접에서 복잡한 데이터 구조에 인덱싱을 추가해서 시스템 속도를 올린 경험을 어필할 수도 있지.

PostgreSQL의 JSON 인덱스 공식 문서는 여기에서 볼 수 있어. 예제나 자세한 내용이 궁금하면 꼭 참고해봐!

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION