
쿼리가 느려지면 가장 먼저 인덱스를 추가하려는 경우가 많습니다. 인덱스는 원하는 행을 빠르게 찾도록 돕지만 저장 공간을 사용하고 INSERT, UPDATE, DELETE마다 함께 관리돼야 합니다. 잘못 만든 인덱스는 선택되지 않거나 쓰기 성능만 떨어뜨립니다. 오라클과 Microsoft SQL Server에서 인덱스를 설계할 때는 쿼리의 필터와 조인, 정렬 패턴을 실제 실행 계획 및 데이터 분포와 함께 분석해야 합니다.
인덱스가 필요한 쿼리부터 찾기
개발자의 기억보다 운영 지표를 기준으로 시작합니다. 전체 실행 시간이 큰 SQL, 호출당 시간이 긴 SQL, 논리적 읽기가 많은 SQL을 찾고 사용 빈도와 사용자 영향을 함께 봅니다. 한 번 실행되는 보고서보다 매초 수백 번 실행되는 짧은 쿼리가 전체 자원을 더 많이 사용할 수 있습니다. 오라클의 성능 뷰와 AWR, SQL Server의 Query Store 및 실행 통계를 환경에 맞게 활용합니다.
선택도와 데이터 분포 이해하기
서로 다른 값이 많은 고객 ID는 특정 행을 찾는 데 유리하지만 값이 두세 개뿐인 사용 여부 열은 단독 인덱스의 효과가 작을 수 있습니다. 그렇다고 선택도가 낮은 열은 언제나 쓸모없는 것은 아닙니다. 특정 값의 행이 매우 적거나 다른 열과 결합될 때 유용할 수 있습니다. 평균 분포만 보지 말고 값의 쏠림과 NULL 비율, 기간별 변화도 확인해야 합니다.
복합 인덱스의 열 순서
복합 인덱스는 WHERE에서 자주 함께 사용하는 열을 묶지만 순서가 중요합니다. 일반적으로 동등 조건 열과 범위 조건, 정렬 요구를 함께 고려합니다. 첫 열을 사용하지 않는 조건은 인덱스를 효율적으로 활용하기 어려울 수 있지만 DB 엔진의 스킵 스캔 등 예외도 있습니다. 단순한 공식보다 실제 쿼리와 실행 계획으로 검증합니다.
설계 체크포인트
- 동등 비교와 범위 비교가 어떤 순서로 사용되는지 확인합니다.
- 조인 열의 자료형과 길이가 양쪽에서 같은지 봅니다.
- ORDER BY를 인덱스 순서로 해결할 수 있는지 검토합니다.
- 포함 열이 필요한지와 인덱스 크기의 균형을 맞춥니다.
- 비슷한 인덱스가 중복돼 있는지 확인합니다.
열에 함수를 적용하면 생기는 문제
WHERE UPPER(name) = ... 또는 날짜 열을 함수로 감싼 조건은 일반 인덱스를 사용하지 못할 수 있습니다. 입력값을 변환하거나 날짜를 시작 이상, 다음 날 미만 범위로 조회하는 방법을 우선 고려합니다. 함수 적용이 업무상 필수라면 오라클의 함수 기반 인덱스나 SQL Server의 계산 열 인덱스를 검토하되 표현식과 세션 설정 조건을 정확히 맞춰야 합니다.
실무 원칙: 인덱스를 추가하기 전에 조건문의 암묵적 자료형 변환과 열 함수 적용 때문에 기존 인덱스가 무효화되는지 먼저 확인해야 합니다.
커버링 인덱스의 장점과 비용
검색 조건뿐 아니라 SELECT에 필요한 열도 인덱스에서 얻으면 테이블 또는 클러스터드 인덱스로 다시 접근하는 비용을 줄일 수 있습니다. SQL Server는 INCLUDE 열을 사용할 수 있고 오라클은 복합 인덱스의 열로 포함하는 방식을 고려합니다. 모든 조회 열을 넣으면 인덱스가 커지고 쓰기 비용과 캐시 사용량이 증가하므로 호출 빈도가 높고 읽기 이득이 큰 쿼리에 제한합니다.
오라클과 SQL Server의 구조 차이
SQL Server의 클러스터드 인덱스는 테이블 데이터의 물리적 구성과 밀접하며 비클러스터드 인덱스가 행을 찾는 방식에도 영향을 줍니다. 오라클의 일반 힙 테이블과 B-tree 인덱스 구조는 다르게 동작하고 인덱스 구성 테이블 같은 별도 선택지가 있습니다. 제품의 용어를 일대일로 대응시키기보다 각 엔진의 행 접근 방식과 실행 계획 연산자를 이해해야 합니다.
필터된 인덱스와 오라클의 조건 기반 함수 인덱스처럼 일부 행만 대상으로 하는 전략도 제품별 문법이 다릅니다. 활성 데이터만 자주 조회하고 과거 데이터가 대부분이라면 큰 효과가 있을 수 있지만 쿼리 조건이 인덱스 정의와 일치해야 합니다.
통계 정보와 실행 계획
옵티마이저는 통계로 행 수와 비용을 추정합니다. 통계가 오래됐거나 값 분포를 제대로 표현하지 못하면 적절한 인덱스가 있어도 좋지 않은 계획을 선택할 수 있습니다. 실행 계획에서 추정 행 수와 실제 행 수의 차이를 확인하고 필요한 경우 히스토그램과 통계 갱신 정책을 검토합니다. 운영 SQL에 힌트를 바로 고정하기 전에 통계와 매개변수 값의 편차를 먼저 살펴봅니다.
인덱스 조각화와 유지보수
페이지 분할과 삭제로 인덱스 상태가 달라질 수 있지만 일정 비율만 보고 모든 인덱스를 매일 재구성하는 것은 과도한 로그와 I/O를 만듭니다. 인덱스 크기, 실제 스캔 방식, 쓰기 부하와 유지보수 창을 고려합니다. 통계 갱신과 인덱스 재구성은 서로 관련 있지만 같은 작업은 아니므로 제품별 동작을 확인합니다.
검증 절차
- 운영 영향이 큰 느린 SQL과 대표 매개변수를 수집합니다.
- 현재 실제 실행 계획과 논리적 읽기를 기록합니다.
- 데이터 분포와 기존 중복 인덱스를 확인합니다.
- 테스트 환경에 후보 인덱스를 만들고 읽기 비용을 비교합니다.
- 쓰기 처리량과 저장 공간 증가도 측정합니다.
- 다른 주요 SQL의 계획이 나빠지지 않았는지 봅니다.
- 배포 후 사용 여부와 전체 자원 변화를 관찰합니다.
사용되지 않는 것처럼 보이는 인덱스를 즉시 삭제해서도 안 됩니다. 월말 작업이나 장애 복구, 외래 키 검사에 드물게 사용될 수 있습니다. 충분한 기간의 사용 통계와 생성 목적을 확인하고, 제거 전 복구 스크립트를 준비합니다. 인덱스는 개별 쿼리 한 개가 아니라 전체 읽기와 쓰기 비용의 균형을 맞추는 데이터베이스 설계입니다.
한 줄 요약: 좋은 DB 인덱스는 공식으로 만드는 것이 아니라 실제 쿼리·데이터 분포·실행 계획과 쓰기 비용을 함께 측정해 선택합니다.