개발일기장

Xuni - StudyGroup 테이블 Index 도입 배경

자몽포도 2023. 4. 27. 14:15

목차

1. StudyGroup Index 도입 배경

2. 결론

3. 성능 테스트


StudyGroup Index 도입 배경

1. StudyGroup 테이블 최대 레코드 수 = 스터디 상품  수 x  호스트 수

2. 사용자의 니즈 - 카테고리 별 그룹 조회

3. 카테고리 컬럼의 cardinality

 

1. StudyGroup 테이블 최대 레코드 수 = 스터디 상품  수 x  호스트 수

 

스터디 그룹 스키마

 

위 테이블을 통해 하고 싶은 말은 해당 테이블 레코드가 기하급수적으로 늘어날 수 있다는 점입니다.

 

예를 들면 회원 한 명이 제가 만든 서비스에 가입한다고 합시다.

그러면 회원 계정을 담은 Member 테이블에 레코드가 한 줄 추가 될 것입니다.

하지만 회원 한 명은 여러 StudyGroup 호스트가 될 수 있습니다.

그 말은 즉, 회원 한 명이StudyGroup 레코드를 여러 줄을 만들 수 있다는 것입니다.

 

2. 사용자의 니즈 - 카테고리 별 그룹 조회

Xuni 프로젝트는 사용자들이 기술 스택을 함께 학습할 수 있는 서비스 제공을 1순위로 하고 있습니다. 

 

서비스가 런칭되면 사용자들은 Xuni에 특정 기술을 함께 학습하기 위해 그룹을 찾으려고 할 것입니다. 예를 들면 MySQL을 학습하기 위해 Xuni를 방문할 것입니다. 다시 말해, 사용자는 특정 기술 별 조회 기능을 필요로 할 것입니다.

 

이제부터 저는 특정 기술이라는 단어를 카테고리라는 말로 바꿔 부르겠습니다. 사용자들은 카테고리 별로 그룹을 조회하고 싶은 욕구가 있을 것 입니다.

 

3. 카테고리 컬럼의 cardinality

현재 category 에서 유니크한 값은 13개입니다. category 컬럼의 유니크 값이 기하급수적으로 증가되진 않습니다. 하지만 인덱스를 적용하기에 그리 낮은 수치는 아니라고 판단합니다. 13개 카테고리가 스터디 그룹에 고루게 있다고 판단했을 때 한 테이블은 약 7% 정도를 차지합니다. 7% 데이터를 불러오는데 테이블 풀 스캔을 하는 것보다 인덱스 기반의 스캔을 하는 것이 훨씬 비용이 저렴할 것입니다.

 

또한 다른 가능성을 생각해볼 수 있습니다. 만약 서비스가 확장될수록 카테고리의 cardinality 또한 천천히 증가할 것입니다. 현재 대부분의 cateogory 는 Java/Spring을 사용하는 개발자들에게 초점이 맞추어 있습니다. node.js 혹은 프론트앤드 영역의 개발자가 Xuni에 대한 니즈가 있다면 카테고리를 추가해야 할 것입니다. 

 

정리하면

1. 현재 카테고리 컬럼의 카디널리티는 인덱스를 적용해서 손해볼만큼 낮지 않다.

2. 카테고리 컬럼의 카디널리티는 증가될 가능성이 존재한다. 

 

결론

2, 1, 3을 차례로 결론내면 이렇습니다.

 

2번 결론 : 사용자는 카테고리 별 조회 기능에 대한 니즈를 가질 가능성이 높다.

1번 결론 : 스터디 그룹 테이블 레코드는 기하급수적으로 증가할 가능성이 높다. 특정 레코드를 찾기 위해 테이블 풀 스캔을 하는 것은 비용이 많이 들 것이다. 조회 성능 향상을 위한 고민이 필요할 수 있다.

3번 결론 : 카테고리 컬럼의 카디널리티를 고려했을 때 인덱스로 설정해도 된다고 생각한다. 인덱스를 통해 조회 성능 향상을 기대할 수 있다.

 

 


성능 테스트

스터디 그룹 총 레코드 수 392340

조회 조건을 만족하는 레코드 22245

 

실행 쿼리

# 인덱스 적용
explain analyze
select * from study_group sg where sg.category = 'JPA';

# 인덱스 미적용
explain analyze
select * from study_group sg ignore index(`study_group_category`) where sg.category ='JPA';

 

인덱스 적용 시 실행 계획 분석 내용

# -> Index lookup on sg using study_group_category (category='JPA')
# (cost=7103.25 rows=40980) (actual time=0.271..66.812 rows=22245 loops=1)

 

인덱스 미 적용 시 실행 계획 분석 내용

# -> Filter: (sg.category = 'JPA')  
# (cost=36445.75 rows=35444) (actual time=0.057..442.716 rows=22245 loops=1)
# -> Table scan on sg  (cost=36445.75 rows=354440)
# (actual time=0.051..400.190 rows=392340 loops=1)

 

인덱스 적용 시 평균 수행 시간 66ms

인덱스 미적용 시 평균 수행 시간 442ms + 400ms = 842ms