Covering Index 기반 게시글 목록 조회 개선
문제
SELECT article_id, title, content, board_id, writer_id, created_at, modified_at
FROM article
WHERE board_id = :boardId
ORDER BY article_id DESC
LIMIT :pageSize OFFSET :offset;
- 페이지 번호가 커질수록 앞쪽 게시글을 순서대로 건너뛰는 OFFSET 비용 증가
- 제목과 본문을 함께 조회하므로 반환하지 않을 게시글도 Clustered Index에서 읽는 문제
해결 전략
- 목록 조회를 ID 선조회와 원본 조인 두 단계로 분리
- 앞 단계는
board_id와article_id만 읽어 건너뛸 게시글의 원본 접근 제거 - 화면에 표시할 30건만 원본과 조인해 제목과 본문 조회
기술 선택 이유
-
Keyset Pagination - 제외
- OFFSET 없이 일정한 조회 성능을 얻지만 특정 페이지 번호로 바로 이동할 수 없음
- 현재 페이지 번호 이동 UI를 유지해야 해 우선 적용 대상에서 제외
-
Covering Index - 채택
- 페이지 번호 이동 UI를 그대로 유지한 채 적용 가능
- 클라이언트가 보내는 파라미터와 응답 형태를 바꾸지 않고 쿼리만 교체
구현
SELECT article.*
FROM (
SELECT article_id
FROM article
WHERE board_id = :boardId
ORDER BY article_id DESC
LIMIT :limit OFFSET :offset
) page
JOIN article ON page.article_id = article.article_id;
- 건너뛸 게시글은 제목과 본문을 읽지 않고 ID만 조회
검증
페이지 100,000 - Covering Index 적용 전후
| 방식 | 실행 시간 | 조회 범위 |
|---|---|---|
| 기존 목록 조회 | 약 3.8s | 건너뛸 게시글도 원본에서 조회 |
| Covering Index 적용 | 약 0.3s | 최종 30건만 원본에서 조회 |
- 페이지 크기 30, OFFSET 2,999,970 동일 위치에서 실행 시간 약
3.8s → 0.3s로 단축
한계
- Covering Index 적용 후에도 OFFSET 탐색 비용은 남음. 페이지 500,000에서 실행 시간 9.42초
- 번호 페이지 UI를 유지하느라 Keyset은 보류. 무한 스크롤이나 연속 탐색이 중요해지면 전환 필요