데이터베이스 인덱스는 테이블 전체를 처음부터 끝까지 읽는 대신, 정렬된 키와 행 위치 정보를 이용해 필요한 데이터를 빠르게 찾도록 돕습니다. 다만 인덱스는 공짜가 아니므로 검색 성능과 저장 공간, 쓰기 성능 사이의 균형이 핵심입니다.
📌 핵심 5줄 요약
1. 인덱스는 검색 대상 열과 원본 행의 위치를 정리한 별도 자료구조입니다.
2. 인덱스가 있으면 조건에 맞는 행을 빠르게 좁힐 수 있지만 항상 사용되는 것은 아닙니다.
3. 인덱스는 별도 저장 공간을 차지하고 INSERT·UPDATE·DELETE 때 함께 갱신됩니다.
4. 복합 인덱스는 열의 순서가 중요하며, 일반적으로 왼쪽 열부터 검색 조건에 활용됩니다.
5. 인덱스 개수는 많을수록 좋은 것이 아니라 실행 계획과 실제 사용 패턴으로 판단해야 합니다.
📌 목차
01 | 인덱스란 무엇인가
02 | 테이블 스캔과 인덱스 스캔
03 | B-Tree 인덱스의 탐색 원리
04 | 인덱스가 검색을 빠르게 만드는 이유
05 | 인덱스와 저장 공간의 관계
06 | 쓰기 작업에 생기는 비용
07 | 선택도와 인덱스 효율
08 | 단일 인덱스와 복합 인덱스
09 | 복합 인덱스의 열 순서
10 | ORDER BY와 인덱스
11 | 커버링 인덱스
12 | 인덱스를 사용하지 않는 경우
13 | 실행 계획으로 확인하는 방법
14 | 실무 인덱스 설계 체크포인트
01 | 인덱스란 무엇인가
인덱스는 테이블의 특정 열 값을 정리하고 해당 값이 있는 행을 찾을 수 있도록 만든 별도 구조입니다. 책 뒤쪽의 색인에서 단어를 찾은 뒤 해당 페이지로 이동하는 것과 비슷합니다.
예를 들어 회원 테이블에서 이메일로 사용자를 찾는 조회가 많다면 email 열에 인덱스를 만들 수 있습니다. 데이터베이스는 이메일 값을 기준으로 정리된 인덱스를 확인한 뒤 원본 테이블에서 필요한 행을 가져옵니다.
02 | 테이블 스캔과 인덱스 스캔
인덱스가 없으면 데이터베이스는 조건에 맞는 행을 찾기 위해 테이블의 여러 행을 순서대로 확인합니다. 이를 전체 테이블 스캔이라고 하며, 데이터가 많을수록 읽어야 할 범위가 커집니다.
인덱스 스캔은 먼저 인덱스에서 조건에 맞는 키를 찾고, 그 키가 가리키는 원본 행을 읽는 방식입니다. 검색 결과가 전체 행 중 일부일 때 특히 유리합니다.
반대로 대부분의 행을 반환하는 쿼리라면 인덱스를 거쳐 원본 테이블을 반복해서 읽는 것보다 전체 테이블을 한 번에 읽는 편이 더 효율적일 수 있습니다.
03 | B-Tree 인덱스의 탐색 원리
많은 관계형 데이터베이스에서 일반적인 검색에는 B-Tree 계열 인덱스가 사용됩니다. 값이 한쪽으로 치우치지 않도록 트리 형태로 정리하고, 각 단계에서 탐색 범위를 줄여 갑니다.
예를 들어 수백만 개의 숫자 중 특정 값을 찾을 때 처음부터 모든 값을 비교하지 않고, 중간 범위에서 어느 가지로 내려갈지 판단합니다. 트리의 높이가 크게 증가하지 않기 때문에 데이터가 커져도 탐색 횟수를 비교적 적게 유지할 수 있습니다.
B-Tree는 같은 값 찾기뿐 아니라 특정 범위 찾기, 정렬된 순서의 읽기에도 활용할 수 있다는 점이 장점입니다.
04 | 인덱스가 검색을 빠르게 만드는 이유
핵심은 데이터베이스가 읽어야 하는 페이지와 행의 수를 줄이는 데 있습니다. 인덱스는 검색 키를 정렬된 상태로 관리하므로 조건에 해당하지 않는 많은 데이터를 건너뛸 수 있습니다.
예를 들어 전체 상품 중 특정 카테고리의 판매 중인 상품만 찾는 경우, 관련 인덱스가 있으면 해당 범위에 가까운 위치로 바로 이동한 뒤 필요한 구간만 읽을 수 있습니다.
다만 검색 속도는 인덱스의 존재만으로 결정되지 않습니다. 데이터 분포, 조건의 형태, 반환 행의 수, 캐시 상태, 조인 방식, 통계 정보에 따라 실행 계획이 달라집니다.
05 | 인덱스와 저장 공간의 관계
인덱스는 원본 테이블에 표시만 붙이는 기능이 아니라 별도로 저장되는 자료구조입니다. 따라서 인덱스 키와 행을 찾기 위한 정보가 추가로 디스크와 메모리를 사용합니다.
인덱스에 포함되는 열이 길거나 여러 개일수록 인덱스의 크기도 커질 수 있습니다. 긴 문자열 열, 여러 열을 포함한 복합 인덱스, 출력 열까지 담은 커버링 인덱스는 검색에는 도움이 될 수 있지만 저장 비용을 함께 검토해야 합니다.
인덱스가 많으면 데이터베이스 백업, 복구, 메모리 캐시 운영에도 영향을 줄 수 있습니다. 정확한 크기는 데이터베이스 제품, 자료형, 저장 엔진, 행 수에 따라 달라지므로 실제 환경에서 확인해야 합니다.
06 | 쓰기 작업에 생기는 비용
테이블에 행을 추가하거나 인덱스 대상 열을 변경하면 관련 인덱스도 갱신해야 합니다. INSERT는 새 인덱스 항목을 추가하고, UPDATE는 기존 위치를 변경하거나 새 위치에 다시 기록할 수 있으며, DELETE는 인덱스 항목을 제거합니다.
따라서 조회가 많은 시스템에서는 인덱스가 큰 이점이 될 수 있지만, 로그 수집이나 대량 적재처럼 쓰기가 중심인 시스템에서는 불필요한 인덱스가 처리량을 떨어뜨릴 수 있습니다.
인덱스는 검색 속도만 보고 추가하지 말고 해당 테이블의 읽기와 쓰기 비율을 함께 봐야 합니다.
07 | 선택도와 인덱스 효율
선택도는 특정 조건이 전체 데이터 중 얼마나 적은 행을 골라내는지를 이해하는 개념입니다. 일반적으로 고유한 값이 많고 조건 결과가 적을수록 인덱스가 검색 범위를 줄이는 데 유리합니다.
예를 들어 사용자 식별자처럼 거의 중복되지 않는 열은 특정 사용자를 찾는 데 효과적입니다. 반면 성별이나 사용 여부처럼 값 종류가 적은 열은 단독 인덱스로도 도움이 될 수 있지만, 데이터 분포와 쿼리 형태에 따라 효율이 낮을 수 있습니다.
선택도가 낮다고 무조건 인덱스가 쓸모없는 것은 아닙니다. 다른 열과 결합하거나 특정 범위 조회, 정렬, 조인에 사용되는 경우 유용할 수 있습니다.
08 | 단일 인덱스와 복합 인덱스
단일 인덱스는 하나의 열을 기준으로 만들고, 복합 인덱스는 두 개 이상의 열을 함께 정렬해 관리합니다.
다음과 같은 조회가 반복된다고 가정해 보겠습니다.
WHERE customer_id = ? AND created_at > ?
이 경우 (customer_id, created_at) 형태의 복합 인덱스는 고객별 데이터를 먼저 좁힌 뒤 시간 범위를 탐색하는 데 활용될 수 있습니다.
단일 인덱스 여러 개가 항상 복합 인덱스를 대신할 수 있는 것은 아닙니다. 데이터베이스가 여러 인덱스를 결합할 수도 있지만, 하나의 적절한 복합 인덱스가 더 효율적인 경우가 많습니다.
09 | 복합 인덱스의 열 순서
복합 인덱스는 열을 어떤 순서로 배치하느냐가 중요합니다. 일반적으로 첫 번째 열이 탐색의 출발점이 되며, 이후 열은 앞쪽 열의 조건으로 범위가 좁혀진 뒤 활용됩니다.
(category, price) 인덱스는 카테고리 조건과 가격 조건을 함께 사용하는 쿼리에 적합할 수 있습니다. 또한 카테고리만 조건으로 사용하는 쿼리에도 활용될 가능성이 있지만, 가격만 조건으로 사용하는 쿼리에서는 기대한 방식으로 사용되지 않을 수 있습니다.
열 순서는 단순히 고유값이 많은 순서로 정하는 것이 아니라 실제 WHERE, JOIN, ORDER BY 조건과 함께 결정해야 합니다.
10 | ORDER BY와 인덱스
인덱스는 검색뿐 아니라 정렬에도 활용될 수 있습니다. 인덱스가 이미 원하는 순서로 정리되어 있고 쿼리 조건이 그 구조와 맞으면 별도의 큰 정렬 작업을 줄일 수 있습니다.
예를 들어 WHERE user_id = ? ORDER BY created_at DESC 형태가 자주 사용된다면 (user_id, created_at) 인덱스가 검색과 정렬을 함께 지원할 수 있습니다.
다만 정렬 방향, 범위 조건, 선택한 열, 데이터베이스 제품에 따라 실제 실행 계획은 달라질 수 있으므로 반드시 실행 계획을 확인해야 합니다.
11 | 커버링 인덱스
커버링 인덱스는 쿼리에 필요한 검색 조건과 출력 열을 인덱스 안에 함께 포함한 구조입니다. 데이터베이스가 인덱스만 읽고 결과를 반환할 수 있다면 원본 테이블을 다시 찾아가는 작업을 줄일 수 있습니다.
예를 들어 고객 번호로 주문을 찾고 주문일과 상태만 출력한다면 검색 열과 출력 열을 적절히 포함한 인덱스를 고려할 수 있습니다.
하지만 출력 열을 무조건 많이 넣으면 인덱스 크기와 쓰기 비용이 커집니다. 자주 실행되는 소수의 쿼리에 대해 효과를 측정한 뒤 적용하는 편이 안전합니다.
12 | 인덱스를 사용하지 않는 경우
조건이 인덱스 열에 적용되더라도 데이터베이스가 항상 인덱스를 선택하는 것은 아닙니다. 결과 행이 너무 많거나 전체 테이블을 읽는 편이 더 싸다고 판단하면 테이블 스캔을 선택할 수 있습니다.
인덱스 열에 함수를 적용하거나 자료형 변환이 발생하는 조건, 앞부분 와일드카드가 있는 문자열 검색, 부적절한 복합 인덱스 순서도 인덱스 활용을 제한할 수 있습니다.
통계 정보가 오래되었거나 데이터 분포가 크게 변한 경우에도 옵티마이저가 적절하지 않은 계획을 선택할 수 있습니다. 인덱스를 추가하기 전에 쿼리 조건과 실행 계획을 함께 분석해야 합니다.
13 | 실행 계획으로 확인하는 방법
인덱스가 실제로 사용되는지 확인하려면 데이터베이스가 제공하는 실행 계획 기능을 사용합니다. 대표적으로 SQL 앞에 EXPLAIN을 붙여 접근 방식, 사용 인덱스, 예상 읽기 범위, 조인 순서 등을 확인합니다.
확인할 항목은 인덱스 이름만이 아닙니다. 예상보다 많은 행을 읽는지, 인덱스를 사용한 뒤에도 원본 테이블 접근이 과도한지, 정렬이나 임시 공간이 발생하는지 함께 봐야 합니다.
개발 환경의 작은 데이터만으로 판단하지 말고 운영 환경과 비슷한 데이터 규모, 분포, 조건으로 테스트해야 합니다.
14 | 실무 인덱스 설계 체크포인트
인덱스 설계는 다음 순서로 접근하면 불필요한 추가를 줄일 수 있습니다.
첫째, 실제로 느린 쿼리와 호출 빈도를 확인합니다. 자주 실행되지 않는 쿼리보다 시스템 전체 부하에 큰 영향을 주는 쿼리를 우선합니다.
둘째, WHERE와 JOIN 조건뿐 아니라 ORDER BY, GROUP BY, 반환 열까지 함께 봅니다.
셋째, 단일 인덱스 여러 개와 복합 인덱스의 차이를 실행 계획으로 비교합니다.
넷째, 인덱스 추가 후 검색 시간뿐 아니라 INSERT·UPDATE·DELETE 처리 시간과 저장 공간도 측정합니다.
다섯째, 사용되지 않거나 중복되는 인덱스는 정리 후보로 관리합니다. 단, 삭제 전에는 실제 사용 여부와 다른 쿼리의 의존성을 확인해야 합니다.
☑ 체크리스트
□ 느린 쿼리를 실행 계획으로 확인했는가
□ 인덱스 대상 열이 실제 검색·조인 조건에 자주 사용되는가
□ 복합 인덱스의 열 순서가 쿼리 패턴과 맞는가
□ 인덱스 추가 후 읽기뿐 아니라 쓰기 성능도 측정했는가
□ 인덱스가 반환 행 수를 충분히 줄이는지 확인했는가
□ 비슷하거나 사용되지 않는 기존 인덱스를 점검했는가
📖 자주하는 질문 Q&A
인덱스를 많이 만들면 검색 속도가 계속 빨라지나요?
그렇지 않습니다. 쿼리와 맞지 않는 인덱스는 사용되지 않을 수 있고, 인덱스가 많아지면 저장 공간과 쓰기 비용이 증가합니다. 실제 실행 계획과 사용 빈도를 기준으로 필요한 인덱스만 유지해야 합니다.
고유값이 적은 열에도 인덱스를 만들 수 있나요?
가능합니다. 다만 단독 인덱스의 효과는 데이터 분포와 쿼리에 따라 달라집니다. 다른 열과의 복합 조건, 정렬, 조인에 활용되는지까지 함께 판단해야 합니다.
복합 인덱스와 단일 인덱스 중 무엇이 더 좋은가요?
정답은 쿼리 패턴에 따라 달라집니다. 여러 조건을 함께 사용하고 특정 순서로 검색하거나 정렬한다면 복합 인덱스가 유리할 수 있지만, 서로 다른 쿼리가 각 열을 독립적으로 사용한다면 단일 인덱스가 더 적합할 수 있습니다.
인덱스가 있는데도 쿼리가 느린 이유는 무엇인가요?
결과 행이 너무 많거나 조건의 형태가 인덱스 구조와 맞지 않을 수 있습니다. 또한 통계 정보, 데이터 분포, 함수 적용, 정렬, 조인 방식 때문에 데이터베이스가 인덱스를 사용하지 않을 수도 있으므로 실행 계획을 확인해야 합니다.
📝 마무리
데이터베이스 인덱스는 검색 범위를 줄여 읽기 성능을 높이는 대신 저장 공간과 쓰기 비용을 추가하는 구조입니다. 좋은 인덱스는 많이 만든 인덱스가 아니라 실제 쿼리 패턴과 데이터 분포에 맞고, 실행 계획으로 효과가 확인된 인덱스입니다.
