안녕하세요. 메인페이지 피드 조회 api 성능 개선에 대해 발표하겠습니다.
먼저 문제상황입니다.
DB에 데이터가 많을 때 피드 조회 api의 성능이 낮음을 스트레스 테스트를 통해서 발견했습니다.
테스트 환경은 데이터베이스는 MYSQL을 사용하고 있고, feed 테이블의 데이터 수 30만개, rss_accept 테이블의 데이터 3000개의 기준으로 테스트를 진행했습니다. rss_accept 테이블을 rss가 등록된 블로그들이고, feed 테이블을 이 블로그들의 게시글 정보가 저장된 테이블입니다.
테스트 결과는 초당 10회의 요청에 대해서 평균 3초, p95값의 경우 5초 정도의 시간이 걸렸고
초당 5회의 요청이 평균 응답 시간, p95, p99 값이 비슷하게 가장 많은 요청을 처리할 수 있는 테스트 환경이었습니다.
해당 문제의 원인을 파악하기 위해 메인페이지 로드시 요청하는 api를 확인했더니 두개의 api 요청이 있었고, 무한 스크롤 기능에 대해서는 feed요청에 쿼리 파라미터가 변경이 되어 요청이 가는걸 확인했습니다.
DB에 데이터가 많아지고 생긴 현상이라 api요청의 쿼리와 실행계획을 확인해봤습니다.
실행계획에서 타입과 엑스트라를 확인해보면, 타입이 all로 풀스캔하고 있고, 테이블 정렬이 디스크 정렬을 사용하고 있었습니다.
filesort가 발생하는 상황에 대해서 찾아보았고, 먼저 feedView 테이블의 인덱스를 확인해보았습니다.
feedView 테이블은 db의 물리적인 테이블이 아닌 가상테이블이였고, MYSQL에는 뷰 테이블에 인덱스를 설정할 수 없음을 알게 되었습니다.
또, feedview 테이블 생성시 order_id값을 계산하기 위해 ROW_NUMBER() 함수를 사용하고 있는데 이 함수가 OVER 절에 있는 모든 데이터를 풀스캔하게 하고 있었습니다.
인덱스를 사용하게 하기 위해 처음에는 가상 테이블을 실제 테이블로 만들어 게시글을 크롤링할 때마다 테이블을 갱신하려고 했지만 새로운 블로그를 등록할 때 order_id값이 변경되는 상황이 발생하여 다른 방법을 찾게 되었습니다.
기존 코드를 확인해보니 feedView 테이블을 사용하는 메서드가 2개였고, feed 테이블의 모든열을 사용하고 rss_accept 테이블에도 노출되지 말아야 할 정보가 없어 가상 테이블대신 feed 테이블에서 조회를 하도록 변경하였습니다.
개선 결과 쿼리 실행 계획이 3단계에서 2단계로 줄었고, 두개의 단계 모두 인덱스를 활용하도록 개선되었습니다.
개선 후 테스트 결과로는 개선 전과 같은 테스트 환경에서 3초에서 2.6밀리세컨드로 개선되었고 초당 500회의 요청에 대해서도 응답속도가 100밀리세컨드 이하로 응답할 수 있도록 개선되었습니다.