- PostgreSQL 쿼리 성능을 높이기 위한 기존 틀을 탈피한 최적화 방법으로 인덱스 추가나 쿼리 재작성이 아닌 3가지 창의적 접근방식 소개
- 체크 제약조건 기반의 전체 테이블 스캔 제거, 함수 기반 인덱스로 낮은 카디널리티 최적화, Hash 인덱스를 통한 유니크 제약 구현
- 1.
constraint_exclusion설정을 활용해 잘못된 조건의 쿼리에서 불필요한 스캔을 방지 - 2. 함수 기반 인덱스와 가상 생성 컬럼(virtual generated column) 을 이용해 인덱스 크기를 줄이고 쿼리 일관성을 확보
- 3. Hash 인덱스와 배타 제약(exclusion constraint) 을 결합해 대용량 텍스트 컬럼의 유니크 제약을 효율적으로 구현, 저장 공간을 크게 절감함
체크 제약조건 기반 전체 테이블 스캔 제거
-
plan컬럼에'free','pro'값만 허용하는 CHECK 제약조건이 있음에도'Pro'로 잘못된 쿼리를 실행하면 PostgreSQL은 전체 테이블을 스캔함- 실행 계획에서 10만 행을 모두 읽으며, 실제 결과는 0행
-
constraint_exclusion파라미터를'on'으로 설정하면 PostgreSQL이 제약조건을 고려해 스캔을 완전히 생략함- 실행 시간이 7.4ms에서 0.008ms로 단축됨
- 기본값은
'partition'이며, 단순 쿼리에서는 오히려 플래닝 오버헤드가 커질 수 있음- 그러나 BI·리포팅 환경에서는 사용자가 잘못된 조건을 자주 입력하므로
'on'설정이 유용함
- 그러나 BI·리포팅 환경에서는 사용자가 잘못된 조건을 자주 입력하므로
함수 기반 인덱스로 낮은 카디널리티 최적화
- 1천만 건의 판매 데이터를 가진
sale테이블에서 일별 매출 집계 쿼리 수행 시 전체 스캔으로 627ms 소요 -
sold_at컬럼에 B-Tree 인덱스 추가 시 187ms로 단축되지만, 인덱스 크기가 214MB로 커짐 -
date_trunc('day', sold_at)표현식에 함수 기반 인덱스를 생성하면 크기가 66MB로 줄고, 실행 시간은 145ms로 더 빨라짐- 낮은 카디널리티 덕분에 인덱스 deduplication이 가능
- 단, 쿼리 표현식이 인덱스 정의와 정확히 일치해야 하므로 표현식 일관성 유지가 필요
- 이를 위해 동일 표현식을 포함한 VIEW를 만들거나,
- PostgreSQL 18부터 지원되는 가상 생성 컬럼(virtual generated column) 을 추가해 일관성을 자동화할 수 있음
- 가상 생성 컬럼을 사용하면 인덱스가 자동으로 활용되며, 작은 인덱스·빠른 쿼리·표현식 일관성을 모두 확보 가능
- 단, PostgreSQL 18에서는 가상 컬럼 인덱스 생성은 아직 미지원이며, 향후 19 버전에서 지원 예정
Hash 인덱스로 유니크 제약 구현
- 긴 URL을 저장하는
urls테이블에서 중복 URL 방지를 위해 B-Tree 기반 유니크 인덱스를 생성하면 인덱스 크기가 154MB에 달함 -
Hash 인덱스는 실제 값을 저장하지 않고 해시값만 저장하므로 훨씬 작음
- PostgreSQL은 기본적으로 유니크 Hash 인덱스를 지원하지 않지만,
-
배타 제약(exclusion constraint) 을 이용해
EXCLUDE USING HASH (url WITH =)형태로 유니크 제약을 우회 구현 가능
- 이 방식으로도 중복 삽입 시 오류가 발생하며, 쿼리 성능도 B-Tree보다 빠름 (0.022ms vs 0.046ms)
- 인덱스 크기는 32MB로, B-Tree 대비 5배 이상 작음
- 단점:
- 외래키 참조 불가 (
REFERENCES제약 불가능) -
INSERT ... ON CONFLICT구문과의 호환성 제한 -
ON CONFLICT ON CONSTRAINT또는MERGE구문으로 대체 가능
- 외래키 참조 불가 (
- Hash 인덱스는 대용량 텍스트 컬럼의 유니크 보장에 적합하며, 외래키가 필요 없는 경우 공간 효율적 대안으로 유용함