인덱스 (Index)
Q. 데이터베이스 Index에 대해서 설명해주세요.
데이터베이스 인덱스는 테이블의 특정 컬럼에 대한 검색 성능을 최적화하기 위해 설계된 데이터 구조입니다. 마치 책의 색인처럼, 데이터베이스가 방대한 데이터 속에서 원하는 레코드를 빠르게 찾아낼 수 있도록 돕습니다. 인덱스는 선택된 컬럼의 값을 정렬된 형태로 저장하며, 이를 통해 전체 테이블을 순차적으로 스캔(Full Table Scan
)하지 않고도 필요한 데이터를 효율적으로 조회할 수 있습니다. 예를 들어, 수백만 행의 고객 테이블에서 특정 이메일 주소를 찾을 때, 인덱스가 없으면 모든 행을 확인해야 하지만, 인덱스가 있다면 단 몇 번의 탐색으로 결과를 반환합니다.
하지만 인덱스는 장점만 있는 것이 아닙니다. 데이터 삽입(INSERT
), 수정(UPDATE
), 삭제(DELETE
) 작업이 발생할 때마다 인덱스도 함께 갱신되므로 쓰기 성능에 부담을 줄 수 있습니다. 또한, 인덱스는 별도의 저장 공간을 차지하므로 디스크 사용량이 증가합니다. 실무에서는 적절한 인덱스를 설계해야 합니다. 예를 들어, 읽기 중심의 분석용 데이터베이스라면 인덱스를 적극 활용하고, 쓰기 중심의 트랜잭션 시스템이라면 최소화하는 식으로 전략을 세웁니다.
Q. Index를 설정하면 내부적으로 어떻게 동작하나요?
인덱스를 설정하면 데이터베이스는 해당 컬럼을 기반으로 B-Tree 또는 변형인 B+Tree 데이터 구조를 생성합니다. B-Tree는 키 값과 데이터 위치를 가리키는 포인터로 구성된 노드들이 계층적으로 연결된 트리입니다. 데이터베이스는 이 구조를 활용해 검색 요청이 들어오면 루트 노드에서 시작해 키 값을 비교하며 하위 노드로 이동, 최종적으로 원하는 데이터의 물리적 위치를 찾아냅니다. 예를 들어, WHERE id = 100
쿼리가 실행되면 B-Tree는 'id'
값을 기준으로 트리를 탐색해 해당 레코드의 주소를 즉시 반환합니다.
B-Tree 외에도 데이터베이스 엔진에 따라 해시 테이블이나 비트맵 같은 다른 구조를 사용할 수 있지만, B-Tree는 범위 검색과 정렬된 데이터 접근에 강점이 있어 가장 널리 사용됩니다.
Q. B-Tree 구조는 어떻게 되며 인덱스를 사용할 때와 사용하지 않을 때 시간복잡도 차이는 어떻게 되나요?
B-Tree는 다차원 균형 트리로, 각 노드가 여러 키와 자식 노드 포인터를 가집니다. 루트 노드에서 시작해 리프 노드까지 이어지며, 모든 리프 노드는 동일한 깊이를 유지합니다. 이 구조는 디스크 기반 데이터베이스에 최적화되어 있는데, 노드당 많은 키를 저장해 트리의 깊이를 얕게 유지하고, 디스크 I/O를 최소화합니다.
인덱스 사용 시: B-Tree의 탐색은 이진 검색과 유사하지만, 다중 키 덕분에 시간복잡도는 O(log n)입니다. 여기서 n은 레코드 수이며, 로그의 밑은 노드당 키 개수에 따라 달라집니다.
인덱스 미사용 시: 테이블 전체를 순차적으로 스캔해야 하므로 시간복잡도는 O(n)입니다. 데이터가 클수록 성능 차이가 극명해집니다. 예를 들어, 1,000,000행에서 특정 값을 찾을 때 인덱스 없이 최대 1,000,000번 비교해야 하지만, B-Tree 인덱스라면 20번 이내에 완료됩니다.
B-Tree의 변형인 B+Tree는 리프 노드에만 데이터를 저장하고, 리프 노드 간 링크를 추가해 범위 검색 성능을 더 개선합니다. MySQL의 InnoDB 엔진은 B+Tree를 기본으로 사용합니다.
Q. LIKE 검색을 할 때, 인덱스가 항상 적용될까요?
B-Tree 인덱스는 문자열의 접두사(왼쪽부터 시작)를 기준으로 정렬되므로, LIKE 'abc%'
같은 접두사 검색에서는 효과적으로 동작합니다. 예를 들어, WHERE name LIKE 'John%'
는 'John'
으로 시작하는 모든 이름을 빠르게 찾습니다. 그러나 접미사 검색(LIKE '%son'
)이나 중간 검색(LIKE '%oh%'
)에서는 정렬 순서를 활용할 수 없어 인덱스가 적용되지 않습니다. 이 경우 데이터베이스는 Full Table Scan
을 수행하며, 성능이 O(n)에 가까워집니다.
이를 해결하려면 특수 인덱스를 사용해야 합니다:
Full-Text Index: 단어 단위로 텍스트를 분리해 인덱싱하며, 검색 엔진처럼 동작합니다. 예:
MATCH(column) AGAINST('keyword')
.Reverse Index: 문자열을 뒤집어 저장한 후 인덱싱해 접미사 검색을 지원합니다.
Q. 그렇다면 인덱스의 종류에는 어떠한 것이 있나요?
인덱스의 종류는 대표적으로 클러스터드 인덱스(Clustered Index
) 와 넌클러스터드 인덱스(Non-Clustered Index
) 가 있습니다.
클러스터드 인덱스는 테이블의 데이터가 인덱스에 따라 물리적으로 정렬되어 저장되며, 테이블당 하나만 생성할 수 있습니다.
반면, 넌클러스터드 인덱스는 데이터가 별도의 위치에 저장되고 인덱스는 데이터의 위치를 가리키며, 하나의 테이블에 여러 개 생성할 수 있습니다.
이외에도
유니크 인덱스(Unique Index) 는 중복을 허용하지 않으며, 특정 컬럼의 값이 고유하도록 보장합니다.
복합 인덱스(Composite Index) 는 여러 컬럼을 조합해 인덱스를 생성하며, 복합 검색에서 유용합니다.
해시 인덱스(Hash Index) 는 정확한 값 검색에 최적화된 인덱스입니다.
마지막으로, 비트맵 인덱스(Bitmap Index) 는 값의 종류가 적고 데이터 중복이 많은 컬럼에 사용됩니다.
Q. 클러스터드 인덱스와 넌클러스터드 인덱스는 각각 어떠한 상황에 쓰이나요?
클러스터드 인덱스는 데이터를 실제로 정렬해서 저장하는 인덱스기 때문에 범위 검색이 많거나, 정렬된 상태에서 데이터를 자주 조회할 때 효과적입니다. 에를 들어, ORDER BY
나 BETWEEN
같은 쿼리를 자주 쓰는 경우 클러스터드 인덱스가 유리합니다. 기본 키(Primary Key
)로 자주 쓰이는 이유도 여기에 있습니다. 다만, 데이터가 자주 변경되거나 삽입될 경우에는 재정렬 비용이 발생해서 성능이 떨어질 수도 있습니다.
반면에 넌클러스터드 인덱스는 데이터가 원래 위치에 있고, 인덱스만 따로 관리하는 방식이기 때문에 특정 컬럼을 WHERE 조건으로 자주 조회하거나, 조인 성능을 높이고 싶을 때 유용하게 쓰입니다. 예를 들어, WHERE email = 'example@email.com'
같은 쿼리가 많다면, email 컬럼에 넌클러스터드 인덱스를 걸어두면 검색이 훨씬 빨라집니다. 또한, 한 테이블에 여러 개를 만들 수 있어 다양한 검색 조건을 최적화 할 수 있습니다.
Last updated