향해99

챕터 3-3 8주차 부하를 적절하게 축소하기 - 인덱스 (index)

dev_0hoon 2024. 8. 6. 10:03

- 쿼리에 대한 부하를 줄일 수 있는 훈련

- 비즈니스 로직 별 트랙잰션 범위를 파악하고 사이드 이펙트에 대해 고려해 본다.

 > 사이드이펙트란? 의도치 않은 결과로 예로 고혈압약을 먹었는데 머리가 풍성하게 자라나는 것같은 의도하지 않았지만 나타나는 결과들이다.

- 비즈니스를 적절하게 핸들링 할 수 있도록 선후관계를 파악하고, 애플리케이션 이벤트를 활용해 관심사를 분리하도록 개선해 본다.

 

----

 

인덱스란?

 

서비스 개발자의 중요한 역량 중 가장 중요한 것은 데이터베이스를 핸들할 수 있는 역량이다. 그 중 중요한 것이 index이다. 

 

- 인덱스는 조회 성능을 높일 수 있지만 아래 사항들을 고려하여 설계해야 함

  - 한번에 찾을 수 있는 값

  - 인덱스 재정렬 최소화 - 데이터 삽입, 수정이 적은 컬럼

  - 인덱스의 목적은 검색 - 조회에 자주 사용되는 컬럼

  - 너무 많지 않은 인덱스 (약 3~4개) - 인텍스 또한 공간을 차지 함

 

인덱스 Column 의 기준

 - 인덱스 대상 컬럼의 핵심은 높은 카디널리티(Cardinality) (데이터의 중복 수치)

e.g.

향해 수강생 테이블 (이름, 성별, 나이, 계좌번호, ..) 가 있다고 가정해볼 댸,

만약 인덱스 설정을 통해 수강생 ㅗ조회 성능을 높이려고 할때

1) 나이 -> 연령대가 많이 차이나지 않으므로 많은 데이터를 걸러낼 수 없음. ( 대부분 20대인 플랫폼인 경우 인덱스를 걸어도 연령대가 20~30대기 때문에 중복수치가 높다. 그러면 카디널리티가 낮다 라고 할 수 있다.)

2) 성별 -> 50%의의 데이터만 걸러냄으로 유의미하지가 않다.

3) 이름, 계좌번호 -> 중복값이 거의 없으므로 검색 조건과 일치하지 않는 데이터 대부분을 걸러 낼 수 있다.

 

> 우리는 데이터를 구성하고 비즈니스 로직을 위해 조회 로직을 구성할 때, 단건이 아닌 특정 조건에 부합하는 다수의 데이터를 조회하는 것에 목적을 둡니다. 그럼 하나의 컬럼이 아닌 다수의 컬럼에 대해 여러 조회 조건을 활용한다. 이 경우, 아래 사항을 주의해서 설계한다.

 

여러 컬럼을 조합한 Index

- 여러 컬럼으로 이루어진 인덱스에서는 일반적으로 카디널리티가 높은 순으로 배치

CREATE INDEX IDX_P_STATUS ON ORDER_ITEM (product_id, order_status)

 

* db를 두개로 만들어서 테스트 진행

 

테이블 예시

향해 99

해당 데이터는 상품번호가 카디널리티가 높다. (주문 상품번호는 유니크하기에 가장 높다. 하지만 PK는 하나의 인덱스이다. 인덱스가 기본적으로 지정되어 있는 컬럼이다. 대상이 아니다.)

상품번호와 상태를 함께 잡는 복합index 형태를 가지는 것이 좋다.

 

- 복합index

CREATE INDEX IDX_P_STATUS ON ORDER_ITEM (product_id, order_status)

해당 쿼리를 날리게 되면  이런 모양의 별도의 테이블을 만들게 된다.

향해 99

상품번호, 상태를 순차적으로 로우를 가진 테이블이 만들어 진다. (실제로 볼 수 있는 테이블이 아닌, db 자체적으로 관리되는 테이블이다.)

그림과 같이 정렬 기준을 보면 상품번호 - 숫자 오름차순, 상태 - 텍스트 오름차순 으로 만들어졌다.

설정에 따라 내림차순으로 변경 할 수 있는 것으로 알고 있다 한다.

 

 

- 원본 데이터에서 조회할 경우 테이블을 풀스캔한다. 1행부터 100000행까지 다 읽게 된다는 말이다.

- 인텍스 테이블에서는 1001번 조회하면, 1001번 위치에서만 조회를 한다.

> 복합인덱스를 구성한 이유는? 1001번에 주문완료를 찾고싶다면, 위에서 부터 한번에 가져올 수 있었다.

 > 인덱스 테이블을 만들 때 상품번호와 상태의 순서가 중요한가? 생각 해봤을 때에 원하는 모양으로 정렬되어 있는 쪽으로 가면 된다.

 

 

참고로

향해 99

이런 모양의 인덱스는 필요가 없다. 위의 상품번호와 상태에 대한 인덱스를 만들었다면, 동일하게 동작한다. 두개의 컬럼에 대해 동일하게 동작한다.

 

1002번의 상품번호를 찾으려한다? 그럼 무식하게 1001..1002..1003.. 이런식으로 찾지 않는다. 이진탐색으로 1001 찾고 1003 찾고 어? 그럼 전이 1002겠네? 하는식으로 찾는다.

 

이건 필요하다 이커머스의 경우 '상태'는 빈번하게 사용되기 때문이다.

 

향해99

상품번호는 정렬되어있지만, 상태는 정렬되어 있지 않다.

그러므로 상태는 자주 사용한다면 인덱스를 잡아준다.

 

인덱스가 많을 수록 좋을까?

인덱스를 만들수록 같은 모양의 테이블이 여러개 만들어진다고 봐야한다.

만약 주문상품번호 8번 로우가 생길 경우, 여러 인덱스테이블이 재조정되기 때문에 그만큼 디비의 속도가 느려지며 부하가 올 수 있다.

 


 

2. LIKE, BETWEEN, <, > 등 범위 조건의 컬럼은 INDEX가 적용되나 그 뒤 컬럼은 Index 적용 x

 

다음 쿼리에서 index의 순서를 보면 product_id는 정상적이다. 상품번호를 찾으면 된다. 다음으로 ordered_at은 이상하다. 1분 1초의 상황으로 주문이 되었다면, 겹칠 확률이 극히 희박한데 거기에 count와 is_abroad를 또 인덱싱하고 있다. 그 뒤가 의미가 없어지는 인덱스이다. 순서를 바꾸는 것이 좋을 것 같다.

 

3. AND는 ROW를 줄이지만 OR은 비교를 위해 ROW를 늘리므로 Full-Scan 발생 확률이 높다.

= where 절에서 OR 연산을 사용할 떄는 이를 고려하자.

 

 

Sync Schedule Strategy

- 전략적으로 실시간 업데이트나 값을 조회하는 것이 아닌 통계 데이터를 활용하는 방법도 있음

- 앞서 말했던 자주 삽입, 수정되는 데이터에 대해 Index를 적용할 경우 "배보다 배꼽이 커지는" 문제가 발생할 수 있음 (인덱스 또한 공간을 차지 하므로)

 

Sync Schedule

실시간성과 정합성의  Trade-Off를 극복하기 위해 DB I/O를 줄이고 언젠가 정합성이 맞아 떨어지는 환경을 구성하는 전략

- 주기적으로 통계 데이터를 Batch Process 를 통해 취합하고 Sync를 맞추는 테이블을 생성

- 통계 데이터를 조회할 땐 통계용 테이블만 조회 (조회 연산 성능 향상)