설치·접속: PostgreSQL 설치와 접속
부제: tsvector·배열·jsonb에 인덱스를 걸려는데 GIN과 GiST 중 뭘 골라야 할지 매번 헷갈릴 때
-- GIN: 값을 잘게 쪼개 "어떤 문서에 이 토큰이 있나"를 역색인. 전문검색·배열 포함
CREATE INDEX idx_docs_gin ON m5_docs USING gin(doc);
SELECT count(*) FROM m5_docs WHERE doc @@ to_tsquery('english','performance & planner');
-- GiST: 값을 경계 상자(bounding box)로 요약. 범위 겹침·좌표·최근접(<->)
CREATE INDEX idx_pts_gist ON m5_pts USING gist(loc);
SELECT id FROM m5_pts ORDER BY loc <-> point(500,500) LIMIT 5; -- 가장 가까운 5개
-- GIN 전문검색: 50만 문서에서 71428건, 4.7ms 만에 후보 색출
Bitmap Index Scan on idx_docs_gin (rows=71428) Execution Time: 44 ms
-- GiST 최근접(KNN): 50만 좌표 중 가장 가까운 5개를 인덱스 순회로
Index Scan using idx_pts_gist on m5_pts (Order By: loc <-> '(500,500)')
rows=5 Execution Time: 0.081 ms ← GIN으로는 불가능한 연산둘 다 btree로 못 하는 검색을 담당하지만 강점이 갈린다. GIN(Generalized Inverted iNdex) 은 값을 토큰으로 쪼개 "이 토큰 → 이 행들" 역색인을 만든다. 그래서 전문검색(@@), 배열 포함(@>), jsonb 키 검색처럼 한 컬럼 안에 여러 원소가 들어 있고 그중 하나로 찾는 경우에 압도적이다. 검색은 빠르지만 여러 토큰을 갱신해야 해 쓰기·빌드가 무겁다 — 자주 바뀌는 컬럼엔 부담. GiST(Generalized Search Tree) 는 값을 경계 상자로 요약한 균형 트리다. 범위 겹침(&&), 좌표 검색, 그리고 GIN이 절대 못 하는 최근접 이웃(ORDER BY loc <-> point) 과 배타 제약(EXCLUDE)을 지원한다. 요약이라 lossy(재검사 필요)지만 쓰기가 가볍다. 정리하면: 텍스트/배열/jsonb를 많이 읽고 덜 바꾼다 → GIN, 범위·좌표·최근접·배타제약이 필요하다 → GiST.
이렇게도 쓴다
배열 컬럼의 "이 값들을 포함" 검색은 GIN이 정석이다. (조합: 배열 @>)
CREATE INDEX idx_docs_tags_gin ON m5_docs USING gin(tags);
SELECT count(*) FROM m5_docs WHERE tags @> ARRAY[7,12]; -- 4034건, 2.9ms
예약 시간대 겹침 검사는 GiST 범위 인덱스로. (조합: 범위 &&)
CREATE INDEX idx_rsv_gist ON m5_rsv USING gist(during);
SELECT count(*) FROM m5_rsv WHERE during && tsrange('2024-06-01','2024-06-02');
시간대 중복 예약을 DB 차원에서 원천 봉쇄한다(GiST만 가능). (조합: EXCLUDE 제약)
ALTER TABLE m5_rsv ADD CONSTRAINT no_overlap EXCLUDE USING gist (during WITH &&);
jsonb 키/값 검색은 GIN에 jsonb_path_ops로 더 작고 빠르게. (조합: jsonb 연산자 클래스)
CREATE INDEX idx_meta_gin ON m5_docs USING gin(meta jsonb_path_ops);
같은 컬럼에 두 인덱스를 만들어 크기를 나란히 비교한다. (조합: pg_relation_size)
SELECT pg_size_pretty(pg_relation_size('idx_docs_gin')); -- 30 MB (GiST는 32 MB)
전문검색을 자주 쓰면 GIN 리스트 정리를 미뤄 쓰기 부담을 던다. (조합: fastupdate)
CREATE INDEX idx_fast ON m5_docs USING gin(doc) WITH (fastupdate=on);'명령어 > DB' 카테고리의 다른 글
| [PostgreSQL] 인덱스가 있는데 안 타는 이유 — 형변환·함수·OR·낮은 선택도 (0) | 2026.07.23 |
|---|---|
| [PostgreSQL] CREATE INDEX CONCURRENTLY — 운영 중 테이블에 인덱스를 무중단으로 건다 (0) | 2026.07.23 |
| [PostgreSQL] 표현식 인덱스(expression index)로 LOWER(email) 검색을 태운다 (0) | 2026.07.23 |
| [PostgreSQL] 데드락 두 트랜잭션이 서로의 잠금을 기다리다 하나가 강제 중단될 때 (0) | 2026.07.23 |
| [PostgreSQL] 확장 통계(CREATE STATISTICS)로 상관 컬럼 추정 오차 잡기 (0) | 2026.07.23 |