2025년 8월 5일
이론
카테고리 : DB
조회 : 19|3분 읽기

인덱스, 정말 쓰기만 하면 빨라질까?


인덱스, 정말 쓰기만 하면 빨라질까?

많은 개발자들이 인덱스만 잘 걸어두면 성능이 좋아질 거라고 생각합니다. 하지만 인덱스를 걸었는데도 느리거나, 오히려 성능이 더 나빠지는 경험을 한 적은 없으신가요? 이번 글에서는 인덱스가 성능을 악화시키는 원인, 그리고 오라클 힌트절을 이용한 튜닝 전략까지 정리해보겠습니다.

✅ 인덱스, 정말 빠르기만 할까요?

인덱스는 기본적으로 B-Tree(Balanced Tree) 구조를 사용합니다. 검색 속도는 평균 O(log N)으로 상당히 빠르지만, 다음과 같은 상황에서는 인덱스를 타지 않거나, 타도 느릴 수 있습니다.

📌 인덱스를 무력화하는 대표적 상황

옵티마이저는 조건절 내 컬럼 중 일부에만 인덱스가 있어도, 전체 조건의 효율이 낮다고 판단되면 인덱스를 무시하고 풀스캔을 선택할 수 있습니다.
예:
sql
1SELECT *
2FROM USERS
3WHERE GENDER = 'F'        -- 인덱스 있음
4  AND NICKNAME LIKE '%a%' -- 인덱스 없음 (후방 와일드카드)
  • GENDER에는 인덱스가 있지만, NICKNAME은 조건이 비효율적이므로 전체 테이블을 스캔할 수 있습니다.
  • 조건 순서와는 관계없이 옵티마이저는 전체 비용 기준으로 판단합니다.
💡 복합 인덱스를 만들거나 조건을 재설계하여 옵티마이저가 인덱스를 활용하기 쉬운 형태로 리팩토링할 수 있습니다.

📌 인덱스를 걸면 효과적인 상황 예시

다음은 특정 컬럼에 인덱스를 걸었을 때 성능 향상이 큰 실무 상황입니다:
테이블 구조 예시자주 쓰이는 쿼리 조건추천 인덱스 컬럼효과
TB_USER(USER_ID(PK), JOIN_DATE, GENDER, IS_ACTIVE)WHERE IS_ACTIVE = 'Y' AND JOIN_DATE >= SYSDATE - 7(IS_ACTIVE, JOIN_DATE)최신 가입자 필터링 속도 향상, 선별률 우수
TB_LOGIN_LOG(USER_ID, DEVICE, LOGIN_AT)WHERE DEVICE = 'ANDROID' AND LOGIN_AT >= :fromDate(DEVICE, LOGIN_AT)로그 테이블에 적합, 정렬+필터 모두 개선
TB_ORDERS(ORDER_ID(PK), ORDER_DATE, STATUS)WHERE STATUS IN ('PENDING', 'IN_PROGRESS')(STATUS, ORDER_DATE)부정 조건 회피하고 다건 조회 속도 개선
💡 인덱스를 만들기 전에 꼭 생각해야 할 것:
  • 해당 컬럼이 자주 필터 조건에 사용되는가?
  • 정렬이나 그룹핑에 쓰이는가?
  • 중복이 심하지 않은가? (선택도가 높은가?)
  • 결과 레코드 수가 전체의 소수인가? (전체 조회면 인덱스 효과 없음)

🎯 옵티마이저가 인덱스를 선택하지 않는 이유

오라클 옵티마이저는 쿼리 실행 시 내부 통계 정보를 바탕으로 다양한 실행 계획 중 가장 비용이 낮다고 판단되는 경로를 선택합니다. 그러나 이 선택이 항상 우리가 원하는 대로 흘러가지는 않습니다.

📌 옵티마이저의 실행 계획 결정 흐름

  • 통계가 최신이 아닐 경우 잘못된 판단을 할 수 있습니다.
  • WHERE 절 조건이 비효율적이면 인덱스를 회피합니다.
  • 심지어 힌트를 줘도 무시되는 경우도 있습니다 (옵티마이저 판단 우선).

🔧 힌트절로 인덱스 강제 사용하기

옵티마이저가 인덱스를 선택하지 않는 경우, 힌트절을 통해 실행 계획을 유도하거나 강제할 수 있습니다. 이는 복합 조건, 부정 연산자, 함수 사용 등으로 옵티마이저가 인덱스를 무시하는 경우 유용합니다.

📌 힌트절 흐름 구조

✅ 힌트절 사용 예시

sql
1-- UPPER 함수로 인해 인덱스가 무시되는 상황에서 힌트 적용
2SELECT /*+ INDEX(u name_upper_idx) */
3FROM USERS u
4WHERE UPPER(u.name) = 'JOHN';
💡 != 같은 부정 조건도 힌트절로 인덱스를 유도할 수 있지만, 옵티마이저가 여전히 무시할 수 있기 때문에 EXPLAIN PLAN으로 확인은 필수입니다.

🧰 주요 힌트 목록

힌트설명
INDEX(table index_name)특정 인덱스 강제 사용
FULL(table)테이블 풀스캔 유도
USE_NL(a b)Nested Loop Join 유도
LEADING(a b)조인 순서 지정
INDEX_COMBINE(table)비트맵 인덱스 조합 사용
INDEX_SKIP_SCAN(table)복합 인덱스에서 선두 컬럼 없이 접근

✅ 인덱스 잘 쓰는 팁

  • WHERE 절의 컬럼 순서와 인덱스 컬럼 순서를 맞춘다
  • 자주 쓰는 조건 + 정렬 컬럼을 복합 인덱스로 구성한다
  • SELECT * 대신 필요한 컬럼만 선택한다
  • 인덱스 통계 정보(ANALYZE TABLE, DBMS_STATS)를 주기적으로 갱신한다

🌐 다른 DBMS에서도 힌트가 있을까?

Oracle의 힌트절은 가장 강력하고 세밀하게 제어할 수 있지만, 다른 RDBMS들도 각자 유사한 기능을 제공합니다:
DBMS힌트/강제 인덱스 문법 예시지원 형태
Oracle/*+ INDEX(tbl idx) */정통 힌트절 (/*+ */)
MySQL / MariaDBUSE INDEX (idx)키워드 기반 인덱스 유도
SQL ServerWITH (INDEX(idx))테이블 힌트 형태로 제공
PostgreSQLpg_hint_plan 확장 모듈 필요기본 지원 X (비공식)
💡 요약: Oracle만의 전유물은 아니지만, Oracle 힌트절은 가장 직관적이고 강력하게 작동합니다.

🔚 마무리하며

인덱스는 성능을 올려주는 도구이기도 하지만, 잘못 쓰면 오히려 병목이 될 수 있습니다. 그리고 옵티마이저는 “똑똑하지만 완벽하지 않기” 때문에, 때로는 개발자가 직접 힌트를 줘야 할 때도 있습니다.