
CHECK 제약조건으로 풀스캔 날려버리기
보통 데이터베이스 최적화라고 하면 인덱스 걸고, 쿼리 고치고, 정규화 풀고... 이런 뻔한 방법들만 떠올리잖아요. 물론 효과는 있지만, 가끔은 창의적으로 접근하면 훨씬 더 큰 성과를 낼 수 있어요.
제가 오늘 소개할 첫 번째 방법은 바로 CHECK 제약조건을 활용한 최적화예요. 예를 들어볼게요. 유저 테이블에 요금제 컬럼이 있고, 'free'랑 'pro' 두 가지만 들어갈 수 있다고 CHECK 제약조건을 걸어뒀다고 가정해보죠.
그런데 분석가가 실수로 'Pro'(대문자 P)로 검색하면 어떻게 될까요? 당연히 결과는 0건이에요. 문제는 PostgreSQL이 이걸 알면서도 전체 테이블을 스캔한다는 거예요. 10만 건 데이터를 다 뒤져서 "없네요~"라고 대답하는 거죠. 정말 비효율적이죠?
여기서 마법 같은 설정이 등장해요. constraint_exclusion을 'on'으로 설정하면, PostgreSQL이 CHECK 제약조건을 보고 "아, 이건 절대 나올 수 없는 값이네?"라고 판단해서 아예 스캔을 건너뛰어요. 실행 시간이 7.4ms에서 0.008ms로 뚝 떨어지는 거죠. 거의 1,000배 가까이 빨라지는 셈이에요.
PostgreSQL, 왜 이렇게 인기가 많을까?
최근 Stack Overflow의 2024년 개발자 설문조사에 따르면, PostgreSQL은 전 세계 개발자들이 가장 선호하는 데이터베이스 4위에 랭크됐어요. 사용자가 늘어날수록 이런 세세한 최적화가 더 중요해지겠죠.
다만 이 설정을 모든 쿼리에 적용하면 단순 쿼리에서는 오히려 플래닝 시간이 더 걸릴 수 있어요. 그래서 기본값은 'partition'으로 되어 있고요. 하지만 BI 툴이나 리포팅 환경처럼 사람이 직접 쿼리를 짜는 곳에서는 실수가 잦기 때문에, 'on'으로 설정해두면 불필요한 풀스캔을 막아줄 수 있어요.
날짜만 인덱싱하면 크기가 1/3로 줄어든다
두 번째 비법은 함수 기반 인덱스예요. 판매 테이블에 판매 일시와 금액이 있다고 해볼게요. 분석가들이 매일 일별 리포트를 뽑는데, 쿼리가 약 600ms 정도 걸린다고 불평하더라고요.
그래서 sold_at 컬럼에 B-Tree 인덱스를 걸었어요. 실행 시간은 187ms로 줄었지만, 인덱스 크기가 무려 214MB나 됐어요. 테이블 크기의 거의 절반이에요. 이건 좀 과하다 싶더라고요.
여기서 생각을 좀 바꿔봤어요. 분석가들은 일별 리포트만 필요한데, 우리는 밀리초 단위까지 인덱싱한 거예요. 과한 거죠. 그래서 날짜 부분만 인덱싱하는 함수 기반 인덱스를 만들었어요.
결과는? 인덱스 크기가 66MB로 줄었어요. 3배 이상 작아진 거죠. DB-Engines 랭킹에 따르면 2025년 1월 기준 PostgreSQL의 인기 점수는 계속 상승 중인데, 이런 효율적인 인덱스 관리가 더 많은 기업들의 선택을 받는 이유 중 하나예요.
함수 기반 인덱스의 숨겨진 함정
하지만 여기엔 함정이 있어요. 쿼리에서 정확히 똑같은 표현식을 써야만 인덱스를 탈 수 있다는 거예요. date_trunc 대신 ::date로 캐스팅하면 인덱스를 못 써요. 조직 내에서 모든 사람이 똑같은 표현식을 쓰게 강제하는 건 현실적으로 불가능하죠.
그래서 예전에는 뷰를 만들어서 해결했는데, PostgreSQL 18부터는 가상 생성 컬럼(Virtual Generated Column)을 쓸 수 있어요. 테이블에 컬럼을 추가하되, 실제 저장 공간은 차지하지 않고 접근할 때마다 표현식을 계산하는 거예요.
이렇게 하면 누구나 sold_at_date 컬럼을 쓰기만 하면 되고, 자동으로 인덱스를 타게 돼요. 표현식 불일치 문제도 해결되고, 타임존 혼동도 없애고, 인덱스도 작아지고... 일석삼조죠. 아쉽게도 아직 가상 컬럼에 직접 인덱스를 거는 건 지원이 안 돼요. PostgreSQL 19에서는 가능해질 거라고 하네요.
해시 인덱스로 유니크 제약조건 걸기
세 번째는 좀 특이한 방법이에요. URL을 저장하는 테이블이 있다고 해볼게요. 웹페이지 처리는 시간도 오래 걸리고 비용도 많이 들어서, 같은 URL을 두 번 처리하면 안 되잖아요.
그래서 보통 url 컬럼에 UNIQUE 인덱스를 거는데, 이게 B-Tree 방식이에요. 문제는 요즘 URL이 엄청 길다는 거예요. 어떤 웹앱은 아예 앱 상태 전체를 URL에 넣기도 하거든요. 저도 처음엔 이런 URL 보고 깜짝 놀랐어요.
100만 개 URL을 저장했더니, 테이블 크기는 160MB인데 B-Tree 유니크 인덱스가 154MB나 됐어요. 거의 같은 크기예요. B-Tree는 실제 값을 저장하기 때문에 값이 크면 인덱스도 커지는 거죠.
해시 인덱스의 놀라운 효과
여기서 해시 인덱스가 등장해요. 해시 인덱스는 실제 값 대신 해시값만 저장해서 훨씬 작아요. 문제는... PostgreSQL이 유니크 해시 인덱스를 지원 안 해요. 처음엔 막막했는데, 방법이 있더라고요.
Exclusion Constraint라는 특수한 제약조건을 쓰면 돼요. 이렇게 하면 해시 인덱스로 유니크를 강제할 수 있어요. 결과는? 인덱스 크기가 32MB로 줄었어요. 5배 작아진 거죠. 게다가 검색도 더 빨라졌어요.
2024년 Gartner 보고서에 따르면, 데이터베이스 운영 비용의 약 40%가 스토리지에서 발생한다고 해요. 인덱스 크기를 5분의 1로 줄이면 비용도 그만큼 아낄 수 있는 거죠. 요즘 클라우드 비용 부담이 큰 스타트업들한테는 정말 반가운 방법이에요.
단점도 있어요. 외래키 참조가 안 되고, INSERT ON CONFLICT 구문이 좀 까다로워요. 하지만 외래키가 필요 없고, MERGE 문으로 대체 가능하다면 충분히 쓸 만한 방법이에요.
실무에 바로 써먹을 수 있는 팁들
지금까지 소개한 세 가지 방법은 교과서에 잘 안 나와요. 하지만 실무에서는 정말 큰 차이를 만들어낼 수 있어요.
첫 번째, BI 툴 쓰는 곳에서는 constraint_exclusion을 켜두세요. 사람들이 하는 실수를 데이터베이스가 미리 막아줘요. 두 번째, 일별이나 월별 리포트가 많다면 날짜 부분만 인덱싱하세요. 가상 생성 컬럼과 함께 쓰면 금상첨화고요. 세 번째, 큰 텍스트나 URL에 유니크 제약조건이 필요하면 해시 인덱스를 고려해보세요. 스토리지 비용을 확 줄일 수 있어요.
데이터베이스 최적화는 결국 상황에 맞는 도구를 고르는 거예요. 남들이 안 쓴다고 나쁜 방법이 아니에요. 오히려 그래서 더 좋을 수도 있죠. 여러분의 프로젝트에 딱 맞는 방법을 찾아보세요. 작은 설정 하나가 성능을 10배, 100배 향상시킬 수 있다는 걸 기억하시고, 오늘 소개한 방법들 한번 시도해보시길 바라요!
'IT > 소프트웨어' 카테고리의 다른 글
| 🚨 2026년, MySQL 쓰면 안 되는 진짜 이유 (0) | 2026.02.17 |
|---|---|
| 🚨 2026년, 데이터 엔지니어가 사라진다 (AI가 아니라 우리 때문에) (0) | 2026.02.17 |
| 🔍 2026년, 플랫폼 엔지니어가 꼭 봐야 할 관찰성 도구 10선 (2) | 2026.02.13 |
| 🤖 AI 시대, 엔지니어의 진짜 정체는 무엇일까? (1) | 2026.02.13 |
| 🍪 대부분의 웹사이트는 사실 쿠키 동의 팝업이 필요 없습니다 (0) | 2026.02.13 |