최근 인덱스를 공부하면서 어떨 때 인덱스를 사용해야 하는가에 대한 나만의 기준을 세우기 위해 노력했다.
간단하게 정리하자면 인덱스가 되는 컬럼의 기준은
1. Where절 또는 join절에서 잦은 사용
2. 낮은 수정 빈도
3. 데이터의 중복(카디널리티 수치)이 적은
정도이다.
어느 정도 기준을 세웠으니 코드에 적용해보자.
테이블에 인덱스를 설정한다는 것은 그만큼의 리소스를 활용하기 때문에 200% 잘 활용해야 한다.
당근 마켓
프로젝트에서 사용자들은 특정 게시물을 조회하는 기능
을 가장 많이 사용할 것이고, 가장 중요한 API라고 생각하였다. 해당 코드를 가장 생각하며 튜닝해야 한다고 생각하였다.
성능에 비교를 위해 1개의 게시물 내에 100개의 댓글 내에 100개의 대댓글 총 10000개의 데이터를 넣어주었다.
🏃 프로시저를 통해 대량의 데이터 세팅하기
SQL을 생성해주는 사이트들이 많지만 프로시저를 통해서 데이터를 등록하였다.
# reply insert 프로시저 DELIMITER $$ DROP PROCEDURE IF EXISTS insertLoop$$ CREATE PROCEDURE insertLoop() BEGIN DECLARE i INT DEFAULT 1; DECLARE j INT; WHILE i <= 100 DO SET j = 1; WHILE j <=100 DO INSERT INTO devcourse.comments(content, status, member_id, post_id, comment_group, seq, created_at, updated_at) VALUES (concat('Reply',j), 'ALIVE',FLOOR(RAND() * 10) + 1, 1, i, j, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); SET j = j + 1; END WHILE; SET i = i + 1; END WHILE; END$$ DELIMITER $$ CALL insertLoop; $$
본격적으로 쿼리를 분석해보자
SELECT * FROM comments c WHERE c.post_id=1 AND c.seq=0
SELECT * FROM comments c WHERE c.comment_group= 1 AND c.seq > 0 ORDER BY c.seq ASC;
프로젝트에서 게시물을 조회할 경우 두 개의 쿼리문이 발생한다.
SELECT
CONCAT(ROUND(COUNT(DISTINCT id) / COUNT(*) * 100, 2), '%') AS id_cardinality,
CONCAT(ROUND(COUNT(DISTINCT content) / COUNT(*) * 100, 2), '%') AS content_cardinality,
CONCAT(ROUND(COUNT(DISTINCT status) / COUNT(*) * 100, 2), '%') AS status_cardinality,
CONCAT(ROUND(COUNT(DISTINCT member_id) / COUNT(*) * 100, 2), '%') AS member_id_cardinality,
CONCAT(ROUND(COUNT(DISTINCT post_id) / COUNT(*) * 100, 2), '%') AS post_id_cardinality,
CONCAT(ROUND(COUNT(DISTINCT comment_group) / COUNT(*) * 100, 2), '%') AS comment_group_cardinality,
CONCAT(ROUND(COUNT(DISTINCT seq) / COUNT(*) * 100, 2), '%') AS seq_cardinality
FROM comments; # 카디널리티 수치 확인하기
카디널리티 수치를 확인해보면 comment_group이 더 높다는 것을 알 수 있다.
comment_group
을 인덱스로 잡아서 성능을 개선해보자
CREATE INDEX idx_comment_commentGroup ON comments ( comment_group );
EXPLAIN
SELECT * FROM comments c WHERE c.comment_group= 1 AND c.seq > 0 ORDER BY c.seq ASC;
MySQL의 Profiling을 통해서 쿼리의 수행시간을 확인할 수 있다.
사전 설정
show index from comments; # 인덱스 확인 show variables like '%profiling%'; #프로파일링 기능 확인 set profiling = 1; # 기능 확성화 set profiling_history_size = 100; # 기록 사이즈 100 SELECT * FROM comments c WHERE c.comment_group= 1 AND c.seq > 0 ORDER BY c.seq ASC; show profiles; # 프로파일링 분석 show profile for query 39; # 쿼리문의 수행 시간을 상세하게 확인 show profile cpu for query 39; # 쿼리문의 CPU 사용량 분석
프로파일링 설정을 하고, 쿼리를 실행한 후 프로파일링을 확인해보면 아래와 같이 39번에서 쿼리문이 발생했다.
그리고 쿼리문의 수행 시간을 분석해보면
[ 속도 비교 ]
인덱스 설정 전 수행 시간의 총합 : 0.065209
인덱스 설정 후 수행 시간의 총합 : 0.006992
테이블을 Full Scan하던 부분을 Index Range Scan을 통해
약 89.31% 성능이 향상 되었다는 것을 알 수 있다.
잘 보고 갑니다. 선배님...