DEVELOPMENT NOTE / Modu Square

대용량 데이터 목록 조회 최적화

문제

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에서 읽는 문제

기술 선택 이유

  • Keyset Pagination — 제외

    • OFFSET 없이 일정한 조회 성능을 얻지만 특정 페이지 번호로 바로 이동할 수 없음
    • 현재 페이지 번호 이동 UI를 유지해야 해 우선 적용 대상에서 제외
  • Covering Index — 채택

    • 페이지 번호 이동을 유지하면서 건너뛸 게시글의 원본 접근 제거
    • board_idarticle_id만 먼저 조회하고 화면에 표시할 30개 ID만 원본과 조인
기존 · OFFSET 조회Secondary Indexboard_id · article_idClustered Indextitle · content 포함offset 0article 0offset 1article 1offset …article …offset 2,999,969article 2,999,969offset 2,999,970article 2,999,970offset …article …offset 2,999,999article 2,999,999건너뛸 행까지 Clustered Index 조회개선 · 커버링 인덱스Secondary Indexboard_id · article_idClustered Indextitle · content 포함offset 0article 0offset 1article 1offset …article …offset 2,999,969article 2,999,969offset 2,999,970article 2,999,970offset …article …offset 2,999,999article 2,999,999필요한 30건만 Clustered Index 조회
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만 조회
  • Secondary Index에서도 OFFSET만큼 ID를 건너뛰는 비용은 유지

측정

page 100,000 — 커버링 인덱스 적용 전·후

방식실행 시간조회 범위
기존 목록 조회약 3.8s건너뛸 게시글도 원본에서 조회
ID 선조회 후 원본 조인약 0.3s최종 30건만 원본에서 조회
  • page size 30, OFFSET 2,999,970에서 같은 위치 비교

page 500,000 — 남은 OFFSET 비용

방식실행 시간조회 범위
커버링 인덱스 + OFFSET9.42s게시글 ID 14,999,970개를 순서대로 건너뜀
같은 위치의 Keyset 조회5.5mslastArticleId 이후 30건만 조회
  • 커버링 인덱스 적용 후에도 깊은 페이지의 OFFSET 탐색 비용은 남음
  • 최신 글부터 보는 서비스 특성상 page 500,000을 직접 조회할 가능성은 낮음

Keyset Pagination 전환 조건

SELECT article_id, board_id, writer_id, title, content, created_at
FROM article
WHERE board_id = :boardId
  AND article_id < :lastArticleId
ORDER BY article_id DESC
LIMIT :pageSizePlusOne;
  • 앞선 게시글을 건너뛰지 않아 깊은 위치에서도 일정한 조회 성능
  • 특정 페이지 번호로 바로 이동할 수 없는 제약
  • 현재는 번호 페이지 유지. 무한 스크롤이나 연속 탐색이 중요해지면 Keyset으로 전환