춤추는 개발자

조회 성능을 책임지는 인덱스 본문

Developer's_til/데이터베이스

조회 성능을 책임지는 인덱스

Heon_9u 2023. 12. 18. 21:12
728x90
반응형

✅ 인덱스(Index)

[ 🛠 간단 요약 ]

조회 기능의 성능을 높이고 싶은 경우,
DB에서 SELECT 시간이 오래 걸리는 경우,
데이터베이스의 검색 속도를 향상시키는 인덱스를 먼저 고려해보자

 

✅ 답답한 조회 시간

 입사 초, 담당하고 있는 웹 사이트에서 조회 시간이 너무 오래걸린다는 문의가 들어왔다. 관리자 포탈에서는 이력 / 통계성 조회 기능이 많다보니, 기간을 길게 설정해서 데이터를 조회하면 DBConnectionTimeOut 에러가 발생하는 경우가 있었다. 하지만, 이번에는 조회 결과가 수만건도 안되는데 조회가 되지 않는 상황이었다.

 

 보통 이런 경우, 시스템에 문제가 없다면, 3가지의 케이스가 있다. 첫번째는 클라이언트(프론트)에서 조회 결과를 한번에 렌더링하는 경우, 두번째는 WAS(백엔드)에서 동작하는 비스지스 로직이나 암/복호화같은 시간이 오래 걸리는 경우, 세번째는 데이터베이스에서 롱쿼리가 발생하는 경우다.

 

 확인 결과, 특정 검색 조건(컬럼)에서 인덱스가 누락된 것이 조회 시간이 오래걸린 원인이었다. 예를 들면 아래와 같이 동적 쿼리 기반으로 조회할 경우, 해당 컬럼에 값이 들어오면 WHERE절이 적용되지만 인덱스가 없어서 조회가 오래걸린 것이었다.

SELECT ID, NAME, AGE
FROM PERSON
WHERE
	AGE >= '20'
<if test=NAME != null>
	AND NAME = 'xxx'
</if>

 결과적으로 누락된 컬럼을 인덱스로 추가하여 문제를 해결할 수 있었다.

 

✅ 인덱스란?

 

 그렇다면 과연, 인덱스가 무엇이길래 위처럼 조회 시간이 오래걸리는 문제를 쉽게 해결할 수 있단 말인가?

 

 

인덱스란 별도의 저장공간을 활용하여 데이터베이스 테이블의 검색 속도를 향상시키기 위한 로우들의 정보로 구성되어 있는 자료 구조이다.

 

 

 예를 들어, 책의 맨 앞 또는 뒤에 목록(색인)을 참고해서 우리가 원하는 책의 내용을 쉽게 찾을 수 있다는 점과 인덱스를 통해 데이터를 빠르게 검색하는 점과 거의 유사하다.

출처: https://mangkyu.tistory.com/96

 

 

인덱스를 단순히 조회하는 SELECT 기능뿐만 아니라, WHERE절을 기반의 DELECT / UPDATE문의 성능을 함께 향상시킨다. 만약, 인덱스가 없는 컬럼을 조회하는 상황이면 테이블 전체를 조회하는 Full Scan이 발생하여 처리 속도가 떨어진다. (Full Scan과 Index Scan과 같은 내용은 추후에 다루도록 하겠다.)

 

✅ 인덱스의 설계 과정

 인덱스를 설계하기 앞서, 인덱스에 영향을 받는 DML문에 대해 알아보자. DBMS는 인덱스를 항상 정렬된 상태로 유지해야 원하는 값을 빠르게 조회할 수 있다. 그래서 만약, 인덱스가 적용된 컬럼에 DML문이 실행되면 아래와 같은 연산들이 추가적으로 수행되며 오버헤드가 발생한다.

 

  • INSERT: 새로운 데이터에 대해 인덱스를 추가 및 정렬
  • DELETE: 삭제하는 데이터의 인덱스를 [사용하지 않음]이라는 작업을 진행
  • UPDATE: 기존의 인덱스를 [사용하지 않음] 처리하고, 갱신된 데이터에 대해 인덱스를 추가

 

그렇다면, 우리는 어떤 컬럼을 기준으로 인덱스를 추가해야 할지 고민할 필요가 있다. 만약 INSERT / DELECT / UPDATE가 빈번한 컬럼에 인덱스를 걸게 되면 인덱스의 크기가 커져서 오히려 성능이 떨어질 수 있다.

 특히, DELETE / UPDATE문은 기존의 인덱스를 삭제하지 않고 [사용하지 않음]작업을 수행한다. 만약, 특정 테이블에 UPDATE / DELETE가 빈번하게 발생된다면 실제 데이터에 비해 인덱스가 훨씬 많이 존재하여 퀴리문 수행 성능이 떨어질 수 밖에 없다.

 

 

[ 인덱스에 적합한 컬럼 ]

 

  • INSERT / DELECT / UPDATE가 자주 발생하지 않는 컬럼
  • 중복도(Cardinality)가 낮은 컬럼
  • JOIN / WHERE / ORDER BY 등 쿼리절에 자주 사용되는 컬럼
  • 규모가 큰 테이블

✅ 인덱스의 자료 구조

 인덱스를 위한 다양한 자료구조가 있지만, 대표적으로 해시 테이블과 B+Tree에 대해 알아보자.

 

[ 해시 테이블 ]

 해시 테이블은 (Key, Value) 쌍으로 데이터를 저장하는 자료구조 중 하나로 시간복잡도가 O(1)이며, 빠른 데이터 검색이 가능하다. 해시 테이블 기반의 인덱스는 (Key=컬럼 값, Value=데이터 위치)를 기반으로 구현되어있다.

 하지만, 데이터베이스의 인덱스를 위한 자료구조로는 한계가 있는데, 해시 테이블은 등호(=) 연산에만 특화되어있기 때문이다. 부등호(<, >), LIKE 등과 같은 연산이 빈번한 데이터베이스에서는 검색을 위해 해시 테이블 인덱스는 적합하지 않다.

 예를 들어 WHERE age > 10 and age < 20과 같은 쿼리문은 인덱스의 효과를 볼 수 없다.

 

 

[ B+Tree ]

 위와 같은 이유로 인덱스 자료구조로는 일반적으로 B+Tree가 사용된다. 자식 노드가 2개 이상인 B-Tree를 개선시킨 자료구조이다.

  • 리프 노드(데이터 노드)들은 LinkedList로 연결되어 있다.
  • 리프 노드만 인덱스와 함께 데이터(Value)를 갖고 있고, 나머지 인덱스 노드들은 데이터를 위한 Key만 갖고 있다.

 데이터베이스의 인덱스 컬럼은 부등호 연산을 이용한 순차 검색 연산이 자주 발생한다. 이러한 특성에 맞춰 B+Tree의 리프 노드들은 LinkedList로 연결하여 순차검색이 용이하게 최적화되었다. B+Tree의 시간복잡도는 O(log2n)이지만 해시 테이블보다 적합한 자료구조가 되었다.

 

출처: https://dataonair.or.kr/db-tech-reference/d-guide/da-guide/?mod=document&uid=297

 

 기존 B-Tree는 Key값이 트리 상에서 한 번만 나타나기 때문에 Key값을 순차적으로 액세스하면서 Tree를 탐색할 때 많은 비용이 발생한다. 이러한 탐색 비용을 최소화하기 위해 B+Tree는 모든 Key값을 리프 노드에 내리고 양방향으로 연결하는 [Double LikedList]로 구성되어있다.

 

 B+Tree의 탐색 성능은 전체 Row수보다 이분화하여 내려가는 깊이에 더 많은 영향을 받는다. 즉, 한 번 이분화했을 때, 처리할 범위가 얼마나 줄었냐에 따라 처리할 깊이에 영향을 준다.

 그런데 스캔은 랜덤 액세스보다 비용 측면에서 더 유리하기 때문에 몇 번의 이분화를 거쳐서 어느정도 처리 범위가 줄어들었다면 더 이상 이분화하지 않고, 스캔을 통해 비교하는 것이 더 유리하다. 이러한 원리를 바탕으로 DBMS들은 몇 개의 컬럼까지만 이분화를 수행하고 나머지 컬럼은 인덱스 로우 의 스캔을 통해 비교한다.

 

 

 이번 포스팅에서는 간단하게 인덱스가 무엇이냐에 초점을 맞췄다. 다음에는 BTree에 대한 내용을 기반으로 인덱스를 설명하고, 옵티마이저에 대해 공부할 것이다.

 

✅ 출처

[Database] 인덱스(index)란?

인덱스 설계 - Data ON-AIR

 

 

 

728x90
반응형