개발

좋아요/싫어요 수 조회 성능 개선 과정

모달조아 2023. 8. 14. 18:39

개요

게시글에 투표(좋아요/싫어요)를 할 수 있는 기능을 추가했다. 이 투표 데이터를 관리하는 방법으로 크게 2가지가 있는데 게시글에 투표 수를 컬럼으로 관리하는 것, 그리고 별도의 투표 테이블을 두는 것이다. 한 회원은 한 게시글에 한 번만 투표를 할 수 있도록 했기 때문에 연관된 회원 정보도 필요했고, 그렇기에 별도의 투표 테이블을 두기로 하였다.

게시글의 투표(좋아요/싫어요) 수를 조회하려면 투표 테이블에서 각각의 수를 count 하면 될 것이다. 다만, 이러면 투표 테이블의 데이터가 많아질수록 조회 성능이 악화될 것인데, 게시글의 투표 수를 조회하는 요청은 게시글 목록, 게시글 상세 조회를 할 때 필요한 정보이니 빈번하게 많이 호출된다. 그러니 한 번 조회 성능을 개선해보도록 하자.

 

대용량 더미 데이터 삽입

일단, 대용량 데이터가 쌓여있다는 가정하에서 개선 작업을 하기위해 더미 데이터가 필요했다. 내 나름의 대용량의 기준부터 정해야했는데 인터넷에 검색해보니 1억, 100만 등 여러가지 의견이 있었다. RDBMS 에서 병목이 생길 수 있는 100만 정도를 기준으로 정했다.

회원에 100만, 게시글 100만, 투표(좋아요/싫어요)에 400만 더미 데이터를 삽입하였다.

더미 데이터 삽입 시간을 최대한 줄이기 위해 bulk insert 문으로 나눠서 삽입하였다. 예를 들자면, 100만 데이터라면 1만개씩 넣는 bulk insert 문을 100번 넣도록 하는 프로시저를 만들었다. 또한 Prepared statement 를 사용하여 더 시간을 줄였다.

 

조회 방식에 따른 조회 시간 비교

최초

현재 게시글의 투표(좋아요/싫어요) 수 조회 로직은 투표 테이블에서 게시글 id 와 좋아요/싫어요 타입을 조건으로 조회한다. 즉, 아래처럼 쿼리가 2번 나간다.

SELECT COUNT(*) FROM review_vote WHERE review_id = ? AND vote_choice = 'LIKE';
SELECT COUNT(*) FROM review_vote WHERE review_id = ? AND vote_choice = 'DISLIKE';

"SELECT COUNT(*) FROM review_vote WHERE review_id = ? AND vote_choice = 'LIKE';" 의 실행 시간을 확인해보면, 아래처럼 약 2초가 나온다. 그렇다면 조회 API 호출 시 약 쿼리 호출 시간으로만 약 4초가 걸린다.

실행계획을 한번 살펴보자.

 full scan 을 통해 조회하는 것을 확인할 수 있다.

 

커버링 인덱스 적용

쿼리에 사용되는 모든 조건을 커버하여 테이블에 접근하지 않고 인덱스만으로 데이터를 판단할 수 있도록 인덱스를 적용해보자. 즉, 연관된 게시글 id 와 투표 종류를 복합 인덱스로 적용하는 것이다. 특정 데이터만 조회할 때는 투표 종류(좋아요/싫어요) 처럼 중복이 많은 데이터는 인덱스를 적용하는 것이 효율적이지 못하지만, 지금처럼 count 쿼리로만 이용하는 경우에는 충분히 유용하다.

복합 인덱스를 적용할 때는 인덱스가 되는 컬럼의 순서가 중요하다. 그 순서대로 정렬을 하기 때문이다. 연관된 게시글을 기준으로 먼저 찾는 것이 유리하므로 (게시글 id, 투표 종류) 로 인덱스를 만들어서 적용하였다.

쿼리 실행 시간이 2초 -> 0.25 초로 87.5% 가량 개선되었다.

적용한 인덱스를 통해 쿼리가 실행되는 것을 확인할 수 있다.

 

게시글에 투표(좋아요/싫어요) 수를 컬럼으로 두는 반정규화

0.25초로 빨라졌다고는 하지만 그럼에도 투표(좋아요/싫어요) 요청 시 쿼리가 2번 날아가 약 0.5초가 걸린다. 투표 수 조회는 정말 빈번히 일어나는 요청이기에 여전히 부담스러운 시간이다.

매번 count 를 하는 방식으로는 대용량 데이터 환경에서 인프라를 건들지 않고서는 한계가 있다. 그래서 게시글에 좋아요 수, 싫어요 수 를 의미하는 컬럼을 두고, 투표 수 조회 시마다 연관된 게시글에서 가져오도록 변경하였다. 이렇게 변경하면, 게시글의 pk 로 조회하게 된다. 쿼리 시간은 0.015 초로 커버링 인덱스를 적용한 것보다도 95%가 개선되었다.

다만, 반정규화를 하면 게시글에 좋아요 수, 싫어요 수가 테이블 2개에 저장되게 되어 데이터 정합성에 문제가 생길 수 있다. 데이터 정합성을 위한 작업을 추가로 해주어야해서 투표 추가/삭제 시에는 기존에 투표 테이블에 insert/delete 하는 방식보다 성능이 안좋아질 것이다. 다만, 투표 삽입/삭제보다 훨씬 빈번히 투표 수 조회 작업이 이루어지기 때문에 이득이라고 생각한다.