들어가며
안녕하세요. LINE VOOM 서비스의 포스트 서버를 개발하고 있는 서용준입니다. 이번 글에서는 저희 팀이 약 7개월에 걸쳐 슬로우 쿼리 문제를 해결한 과정과 그 과정에서 배운 교훈을 공유하고자 합니다.
저희 서비스에서는 헤비 유저의 소셜 프로필을 조회할 때 간헐적으로 슬로우 쿼리가 발생하고 있었습니다. 발생 빈도가 높지는 않았지만 한 번 발생하면 쿼리가 30초 이상 실행되다가 타임아웃이 발생했습니다. 결론부터 말씀드리면, MySQL 8.0.13의 함수형 인덱스(functional index)를 적용하고 쿼리 조건을 변경해 이 문제를 해소할 수 있었습니다. 이 글에서는 문제의 원인을 파악하고 해결책을 찾아가는 과정, 그리고 운영 환경에 적용하면서 겪은 시행착오를 상세히 다루겠습니다. 비슷한 문제를 겪고 계신 분들께 도움이 되길 바라며 시작하겠습니다.
참고: 이 글에서 사용하는 테이블명, 컬럼명, 비트 값 등은 이해를 돕기 위해 간소화한 예시입니다.
문제 상황
어떤 상황에서 슬로우 쿼리가 발생했는지 저희 서비스의 데이터 구조와 발생한 상황을 간략히 짚어보겠습니다.
LINE VOOM 포스트 서비스의 데이터 구조와 테이블의 역할
먼저 저희 서비스의 데이터 구조를 간략히 설명하겠습니다. 저희 서비스는 사용자의 포스트 메타 정보가 여러 샤드에 분산돼 있고, 각 샤드에는 여러 개의 파티션 테이블이 존재합니다. 여기서 주목해야 할 부분은 테이블 속 category_flag와 access_flag 컬럼입니다. 두 컬럼 모두 bit(64) 타입으로, 다양한 상태 정보를 비트 플래그 형식으로 압축 저장하고 있습니다. 예를 들어 category_flag & 0x0100 조건은 프리미엄 콘텐츠를 의미하고, access_flag & 0x0001 조건은 검색 노출 대상 포스트를 의미합니다.
헤비 유저 프로필 조회 시 타임아웃을 발생시켰던 쿼리 색출
문제는 포스트를 수십만 건 보유한 헤비 유저의 소셜 프로필을 조회할 때 쿼리가 30초 이상 실행되다가 타임아웃이 발생하는 것이었습니다. 이 때문에 최대 응답시간 이상치 알림도 빈번하게 울려서 오류 모니터링에 혼선을 빚어내기도 했습니다. 아래는 소셜 프로필을 조회할 때 호출되는 API의 최대 응답 시간 그래프인데, 응답 시간이 길어 임계치가 넘어가는 요청은 빨갛게 표시됩니다. 그래프가 붉게 물들어 있는 것을 볼 수 있습니다.

문제가 발생한 쿼리를 살펴보면 다음과 같습니다.
SELECT post_id, user_id, sort_key, category_flag, access_flag, create_dtm, update_dtm, writer_id
FROM post_metadata
WHERE (user_id = '{user_id}' AND category_flag & 0x0100 AND access_flag & 0x0001)
ORDER BY sort_key DESC LIMIT 70
실행 계획을 분석해 보니 스캔하는 행 수가 수십만 건에 달했습니다. 왜 이렇게 많은 행을 스캔해야 했을까요?