본문 바로가기
● Data Processing

SQL Server 인덱스 구조를 읽으면 쿼리 성능이 달라집니다

by DataFolio.lab 2026. 6. 8.

SQL Server에서 clustered indexnonclustered index는 둘 다 B-Tree 계열 구조를 쓰지만, 저장되는 위치와 leaf level의 의미가 완전히 다르기 때문에 성능 특성도 달라집니다. clustered index는 데이터 행 자체가 인덱스 순서로 정렬되어 저장되고, nonclustered index는 데이터와 분리된 별도 구조로서 row locator를 통해 실제 행을 찾아갑니다. 

SQL Server 인덱스 구조를 읽으면 쿼리 성능이 달라집니다


SQL Server 인덱스를 먼저 이해해야 하는 이유

인덱스는 단순히 “검색을 빠르게 해주는 부가 구조”가 아니라, SQL Server가 데이터를 찾는 경로 자체를 바꾸는 핵심 장치입니다. 적절한 인덱스가 있으면 전체 테이블을 훑는 scan 대신 필요한 위치만 빠르게 찾아가는 seek가 가능해집니다. 

 

특히 조회 성능은 WHERE 조건, JOIN, ORDER BY, GROUP BY 패턴과 강하게 연결됩니다. 그래서 SQL Server 쿼리 최적화의 첫걸음은 실행 계획을 보기 전에, 먼저 어떤 인덱스가 어떤 형태의 접근을 유도하는지 이해하는 데서 시작해야 합니다.


B-Tree 구조의 공통점

SQL Server 문서에서는 일반적으로 B-tree라는 용어를 쓰지만, rowstore 인덱스 내부 구현은 B+ tree로 설명됩니다. 즉 루트, 중간 레벨, leaf 레벨로 내려가며 키 값을 기준으로 탐색하는 계층 구조라는 점이 핵심입니다. 

 

이 구조의 장점은 범위 탐색과 정렬된 탐색에 강하다는 것입니다. 키 순서대로 페이지가 연결되어 있어 특정 값 하나를 찾는 것뿐 아니라, 연속된 값 구간을 읽는 작업에서도 효율적입니다.


Clustered index의 내부 구조

clustered index는 leaf level에 실제 데이터 행이 있습니다. 다시 말해, clustered table에서는 인덱스의 가장 아래 단계가 곧 테이블 데이터이며, 데이터 페이지가 clustered key 순서로 정렬됩니다. 

 

이 때문에 clustered index는 한 테이블당 하나만 둘 수 있습니다. 데이터 행 자체가 하나의 순서로만 저장될 수 있기 때문이며, 이 점이 nonclustered index와 가장 큰 구조적 차이입니다. 

 

예를 들어 clustered key가 OrderDate라면, 데이터는 날짜 순으로 저장되므로 최근 날짜 구간 조회나 날짜 범위 검색에 유리할 수 있습니다. 반면 랜덤한 키 변경이 자주 발생하면 페이지 분할과 재배치 비용이 커질 수 있습니다.


Nonclustered index의 내부 구조

nonclustered index는 테이블 데이터와 분리된 별도 구조입니다. leaf level에는 실제 데이터 행이 아니라 key 값과 row locator가 들어가며, 이 locator가 heap 또는 clustered table의 실제 행을 가리킵니다. 

 

heap인 경우에는 row locator가 행 위치를 가리키고, clustered table인 경우에는 clustered key를 가리킵니다. 그래서 clustered index가 이미 있는 테이블에서는 nonclustered index가 결국 clustered key를 통해 다시 데이터 행으로 이동하는 구조가 됩니다. 

 

이 구조 덕분에 nonclustered index는 여러 개를 둘 수 있습니다. 자주 검색되는 조건 컬럼마다 별도의 인덱스를 구성할 수 있고, 커버링 인덱스 설계를 통해 테이블 본문을 읽지 않고도 쿼리를 끝낼 수도 있습니다.


둘의 차이를 한눈에 보기

항목 Clustered index Nonclustered index
leaf level 실제 데이터 행 key 값과 row locator
테이블과의 관계 데이터 자체가 인덱스 순서로 저장 데이터와 분리된 별도 구조
개수 테이블당 1개 여러 개 가능
강점 범위 조회, 정렬, 순차 읽기 특정 조건 검색, 커버링, 보조 탐색
주의점 키 변경 비용, 페이지 split 과도한 개수 시 쓰기 부담 증가

 

이 차이는 단순한 이론이 아니라 실행 계획과 IO 패턴에 직접 영향을 줍니다. 어떤 쿼리는 clustered index seek가 유리하고, 어떤 쿼리는 nonclustered index seek 후 key lookup이 더 적절합니다.


조회 성능을 좌우하는 설계 포인트

인덱스 설계에서 가장 중요한 것은 “많이 쓰는 조건”과 “읽는 방식”입니다. 단일 값 조회가 많다면 선택도가 높은 컬럼이 유리하고, 넓은 범위를 읽는다면 clustered key 선택이 더 중요해집니다. 

 

또한 WHERE 조건만 보지 말고 ORDER BY, JOIN, TOP, DISTINCT까지 함께 봐야 합니다. 같은 인덱스라도 정렬을 자연스럽게 만족시키면 sort 비용을 줄일 수 있고, 조인 키를 맞추면 hash join 대신 더 가벼운 경로가 가능해집니다. 

 

실무에서는 보통 다음 원칙이 유효합니다.

  • clustered key는 자주 변경되지 않는 값, 좁고 고유한 값, 순차 증가 패턴에 적합합니다.
  • nonclustered index는 자주 조회되는 조건 컬럼이나 조인 컬럼에 둡니다.
  • 포함 컬럼을 활용해 key lookup을 줄입니다.
  • 너무 많은 인덱스는 insert, update, delete 비용을 키우므로 피합니다.

자주 생기는 오해

첫째, clustered index가 “물리적으로 무조건 정렬된 파일”이라는 표현은 단순화된 설명입니다. 정확히는 데이터 행이 clustered key 순서에 따라 B-tree leaf level에 저장된다는 뜻에 가깝습니다. 

 

둘째, nonclustered index는 “데이터 복사본”만은 아닙니다. 실제로는 key와 locator 중심의 탐색 구조이며, 필요에 따라 쿼리를 완전히 충족하는 covering 구조가 될 수도 있습니다. 

 

셋째, 인덱스는 많을수록 좋지 않습니다. 읽기 성능을 올리려다 쓰기 성능과 유지보수 비용이 급격히 나빠질 수 있으므로, 조회 패턴이 명확한 것부터 우선 적용하는 편이 좋습니다.


실전 설계 흐름

가장 좋은 출발점은 자주 실행되는 쿼리를 모으고, 조건절과 정렬, 조인을 기준으로 분류하는 것입니다. 그다음 실행 계획에서 scan이 반복되는지, lookup이 과도한지, sort가 자주 생기는지를 확인해야 합니다. 

 

그 후 clustered index는 테이블의 “기본 이동 경로”로 보고, nonclustered index는 자주 쓰는 질문에 대한 “빠른 색인표”로 생각하면 설계가 쉬워집니다. 이 관점으로 보면 하나의 테이블에 어떤 키를 중심으로 데이터를 정렬할지, 어떤 컬럼을 보조 탐색용으로 둘지 자연스럽게 정리됩니다.


예시

주문 테이블에서 OrderID가 증가형 고유값이고, 조회는 CustomerID, OrderDate로 많이 한다면 clustered key는 OrderID가 무난하고, nonclustered index는 CustomerID, OrderDate 조합이나 자주 필요한 포함 컬럼을 얹는 방식이 실용적입니다. 이렇게 하면 데이터 적재는 안정적으로 유지하면서도 조회는 빠르게 가져갈 수 있습니다. 

 

인덱스 최적화는 결국 “데이터를 어떤 순서로 저장할지”와 “조회가 어떤 경로를 가장 자주 쓰는지”를 맞추는 작업입니다. clustered index와 nonclustered index의 내부 구조를 이해하면, 쿼리 최적화는 감이 아니라 설계가 됩니다. 

반응형

놓치면 아쉬운 추천 글, 함께 읽어보세요!

  • 추천 글을 불러오는 중입니다...