조회속도 개선에서 더 개선하는 방법이 궁금합니다.
안녕하세요. 인덱스를 활용한 조회 성능 개선을 공부하던 중 궁금한 점이 생겨 질문드립니다.
현재 저는 OFFSET 기반 pagination을 사용하는 서비스를 개발하고 있으며, 다음과 같은 환경에서 성능 테스트를 진행했습니다.
데이터: 약 1,000만 건
서버: EC2 t3.small
DB: RDS t4g.micro
k6 vus100
1. 문제 상황
초기에는 OFFSET 제한 없이 마지막 페이지까지 이동 가능하도록 구현했습니다.
하지만 데이터가 1,000만 건 수준으로 증가하자, 깊은 페이지로 갈수록 조회 속도가 급격히 느려지는 문제를 확인했습니다.
2. 고민 및 제약
일반적으로 이 문제는 Keyset Pagination(커서 기반)으로 해결하라고 많이 알려져 있습니다.
하지만 제 서비스는
👉네비게이션 바를 통한 페이지 직접 이동 (ex. 1, 10, 100 페이지 클릭)
이 필요하기 때문에 Keyset 방식만으로는 요구사항을 만족시키기 어렵다고 판단했습니다.
3. 적용한 개선 방법
다음과 같은 방식으로 성능 개선을 진행했습니다.
OFFSET 최대 범위를 제한 (최대 10,000 페이지 / OFFSET 100,000)
커버링 인덱스 적용
조회 방식 개선
먼저 ID만 조회 → 이후 필요한 10건만 상세 조회
전체 게시글 수(count)는 캐싱 처리
4. 성능 개선 결과
[Page 10]
avg: 1.4s → 700ms
p95: 4.5s → 1.8s
[Page 100]
avg: 17s → 1.18s
p95: 24s → 3.3s
[Page 1000]
avg: 32.1s → 1.7s
p95: 59s → 4.27s
SQL 쿼리는 분석결과
약 1700MS -> 70MS 까지 단축한 것 같습니다.5. 추가 제약사항
로그인 사용자와 비로그인 사용자의 조회 결과가 다름
(사용자별 구독 게시글이 포함됨)따라서 캐시는 비로그인 사용자에만 적용
위 성능 수치는 로그인 사용자 기준
6. 현재 고민
위와 같이 개선했지만,
👉 여전히 성능이 충분하지 않다고 느끼고 있습니다.
특히 궁금한 점은 다음과 같습니다.
7. 질문
OFFSET 기반 pagination을 유지하면서
👉추가로 성능을 개선할 수 있는 방법이 있을까요?다음과 같은 방법들을 고려했는데, 방향성이 맞는지 궁금합니다.
RDS를 2개를 사용하여 조회 성능 데이터를 각각 2개의 db가 처리하도록 한다?
Keyset + OFFSET 혼합 방식
(일반적인 페이지 이동은 Keyset Pagination을 사용하고,
사용자가 특정 페이지를 직접 입력하거나 점프하는 경우에만
제한적으로 OFFSET 기반 조회를 사용하는 혼합 방식)RDS 스펙 업그레이드
또한 에펨코리아(https://www.fmkorea.com/)와 같은 대형 커뮤니티는 제가 원하는 페이지 네이션 방식을 사용하면서 깊은페이지(최대 1만)도 지원하고
동시접속자 수십만
페이지 수천~수만
대량 데이터
환경에서도 빠른 조회 성능을 유지하는데
👉이러한 서비스들은 어떤 방식으로 pagination 및 조회 성능을 처리하는지 궁금합니다.
답변 2
1
안녕하세요 영훈님 좋은 질문 감사합니다! 1,000만 건 데이터에서 32초가 걸리던 쿼리를 1.7초까지 줄이신 건 이미 정말 의미 있는 성과인 것 같습니다!!
1. OFFSET 기반을 유지하면서 추가로 줄일 수 있는 포인트
지금 적용하신 방법(커버링 인덱스 + 2단계 조회 + count 캐싱)은 OFFSET 기반에서 할 수 있는 정공법은 거의 다 적용된 상태라고 봐도 될 것 같아요. 그래도 추가로 시도해보시면 효과를 보실만한 부분 몇 가지 짚어드릴게요.
가장 먼저 의심해보실만한 건 RDS t4g.micro의 InnoDB Buffer Pool 크기입니다. t4g.micro는 메모리가 1GiB이고, 기본적으로 buffer_pool은 약 75% 정도가 잡히는데, 1,000만 건 테이블의 인덱스 + 자주 조회되는 페이지 데이터가 이 안에 다 못 들어가면 매 요청마다 디스크 I/O가 발생합니다. 깊은 페이지로 갈수록 새로운 인덱스 페이지를 디스크에서 읽어야 하니 p95가 튀는 원인이 여기 있을 가능성이 큽니다. SHOW ENGINE INNODB STATUS나 innodb_buffer_pool_reads / innodb_buffer_pool_read_requests 비율을 한 번 확인해보시는 걸 권해드립니다. (MySQL 공식 문서 - Buffer Pool)
두 번째는 페이지 번호 → 시작 ID 매핑을 캐싱하는 방식이에요. 예를 들어 100페이지 = id 9,900,001 같은 식으로 페이지 진입점을 미리 계산해서 Redis에 올려두면, OFFSET 100,000을 실제로 건너뛰는 비용 없이 WHERE id <= 9,900,001 ORDER BY id DESC LIMIT 10 형태로 처리할 수 있습니다. 게시글이 삭제되면 약간 어긋날 수 있는데, 그건 주기적으로 재계산하거나 약간의 오차를 허용하는 걸로 가져가시면 됩니다. 이 패턴은 사실상 keyset의 장점을 OFFSET UI 위에 얹는 거라 도전해보실만 해요.
세 번째는 로그인 사용자의 구독 게시글 처리를 분리하는 거예요. 지금 캐싱이 비로그인에만 적용된다고 하셨는데, 만약 "기본 게시글 리스트 + 사용자별 구독 가산 표시"로 분리할 수 있다면 기본 리스트는 전체 캐시를 활용하고, 구독 표시 부분만 in-memory에서 합치는 식으로 처리할 수 있습니다. 이러면 로그인 사용자도 캐시 혜택을 받을 수 있어요.
2. 고려하신 방법들의 방향성 평가
RDS Read Replica를 추가해서 조회 부하를 분산하는 건 동시 처리량(TPS) 측면에서는 효과가 분명히 있습니다. 다만 "단일 쿼리 한 건의 응답 시간"이 빨라지는 건 아니라는 점은 구분해서 보셔야 해요. p95가 4.27초인 게 동시 요청 경합 때문이라면 효과가 클 거고, 단일 쿼리 자체 비용 때문이라면 효과가 제한적입니다. 어느 쪽인지는 vus를 1~5 정도로 줄여서 단일 응답 시간만 한 번 측정해보시면 구분이 가능해요.
Keyset + OFFSET 혼합은 개인적으로 가장 추천드리고 싶은 방향이에요. 사용자의 실제 행동 패턴을 보면 90% 이상은 다음 페이지 / 이전 페이지 이동이고, 직접 페이지 점프는 소수입니다. 다음/이전은 keyset으로 처리하면 페이지 깊이와 무관하게 일정한 속도가 나오고, 점프만 OFFSET으로 처리하면 OFFSET이 발생하는 빈도 자체가 크게 줄어듭니다. 실제로 운영 단계에서 OFFSET 호출 비율이 5% 미만으로 떨어지면, 그 5%가 좀 느려도 전체 사용자 경험 지표는 훨씬 좋아져요.
RDS 스펙 업그레이드는 위에서 말씀드린 buffer pool 부족이 원인이라면 가장 빠르게 효과를 체감하실 수 있는 방법이에요. 다만 이력서/면접 관점에서는 "스펙 올려서 해결했다"는 스토리는 매력도가 떨어지니, 먼저 buffer pool hit ratio를 측정해서 "메모리 부족이 병목임을 확인했고, 스펙 업그레이드와 동시에 캐시 전략으로 hit ratio를 X% → Y%로 올렸다"는 식으로 풀어가시는 걸 추천드려요.
3. 대형 커뮤니티는 어떻게 처리하는가
에펨코리아나 디시 같은 대형 커뮤니티에서 deep page를 어떻게 빠르게 보여주는지에 대한 공개 자료는 많지 않지만, 일반적으로 알려진 패턴들을 묶어 설명드리면 이렇습니다.
가장 큰 차이는 데이터 분리 단위예요. 1,000만 건이 단일 테이블에 있는 게 아니라, 게시판 단위로 분리되어 있거나, 월/년 단위 파티셔닝이 적용되어 있는 경우가 대부분입니다. 한 게시판당 실제 활성 데이터는 수십~수백만 건 수준으로 떨어지고, 그 정도면 인덱스가 메모리에 충분히 올라갑니다. (MySQL Partitioning)
두 번째는 count의 정확도를 포기하는 것입니다. "총 12,847페이지" 같은 정확한 숫자를 보여주는 게 아니라, "10000+" 형태로 처리하거나, 첫 페이지에서 보이는 페이지네이션 범위만 정확히 계산하고 그 이상은 추정값으로 표시합니다. 정확한 count는 1,000만 건에서는 그 자체로 비싼 연산이라 근사치로 대체하는 게 일반적이에요.
세 번째는 인기 페이지(주로 1~10페이지)는 통째로 페이지 단위 캐싱을 적용하는 거예요. 90% 이상의 트래픽이 첫 몇 페이지에 집중되니까, 그 부분만 캐시 hit이 나도 DB 부하의 대부분이 사라집니다. 깊은 페이지는 어차피 트래픽이 적어서 좀 느려도 시스템 전체 영향이 작구요.
마지막으로 검색이 들어가는 경우는 Elasticsearch 같은 별도 검색 엔진으로 빼서 처리합니다. 대부분의 서비스들은 검색 성능을 ES 로 많이 끌어 올립니다. WHERE 조건이 복잡해질수록 RDB 인덱스로는 한계가 분명하기 때문이에요.
4. 이력서/면접 관점에서의 활용
지금 이 경험은 그 자체로 이력서 한 줄 이상의 가치가 충분히 있어요. 예를 들면 이렇게 정리해보실 수 있습니다.
"1,000만 건 게시글 환경에서 deep OFFSET pagination 성능 문제를 분석하고, 커버링 인덱스 + 2단계 조회(ID 선조회 후 상세) + count 캐싱을 조합해 100페이지 응답 시간 17s → 1.18s, 1000페이지 응답 시간 32s → 1.7s로 약 95% 단축"
면접에서 받을만한 질문은 대략 이런 것들이에요.
"Keyset이 아니라 OFFSET을 유지한 이유는?" → 네비게이션 바를 통한 직접 페이지 이동이 핵심 요구사항이었고, 사용자 경험을 위해 트레이드오프를 감수했다고 답하시면 됩니다. 요구사항 분석을 했다는 게 드러나는 답변이라 좋습니다.
"왜 100,000 OFFSET까지만 제한했는가?" → 실제 사용자 행동 로그상 1만 페이지 이상 점프하는 비율이 무시할만한 수준이고, 비용 대비 효용을 고려해 합리적 상한선을 두었다고 답하시면 좋습니다.
"buffer pool은 충분했는가?" → 이게 안 나올 수도 있지만, 위에서 말씀드린 측정을 해두시면 면접에서 한 단계 더 깊은 답변이 가능해집니다.
이번 기회에 buffer pool hit ratio랑 단일 vus 응답 시간까지 측정해보시고, 그 데이터까지 같이 정리해두시면 면접에서 정말 강한 무기가 되실 거예요. 응원하겠습니다!!!!
0
안녕하세요, 인프런 AI 인턴입니다. 문영훈님.
인덱스를 활용한 pagination 성능 개선에 대해 심도 있는 질문을 주셨네요. OFFSET 기반 pagination에서 성능을 더욱 향상시키는 방법을 고민하고 계신데요, 질문하신 내용을 토대로 몇 가지 방법과 개선 방향을 제안드리겠습니다.
OFFSET 기반 Pagination 성능 개선 방안
Keyset Pagination 혼합 사용:
- 이미 제안하신 Keyset + OFFSET 결합 방식은 매우 유용할 수 있습니다. 일반적으로 Keyset 방식이 성능이 뛰어나고, OFFSET은 사용자 편의에 도움이 됩니다. 각 상황에 맞게 두 방식을 조합하면 성능과 편의성 간 균형을 맞출 수 있습니다.
- 이미 제안하신 Keyset + OFFSET 결합 방식은 매우 유용할 수 있습니다. 일반적으로 Keyset 방식이 성능이 뛰어나고, OFFSET은 사용자 편의에 도움이 됩니다. 각 상황에 맞게 두 방식을 조합하면 성능과 편의성 간 균형을 맞출 수 있습니다.
데이터베이스 파티셔닝 및 복제:
- 파티셔닝(Partitioning)을 적용하거나 RDS를 복제하여 읽기 전용 DB로 사용하는 방법도 고려해 볼 만합니다. 데이터 분산을 통해 각각의 DB에 매우 높은 성능을 요구하는 요청을 분배할 수 있습니다.
- 파티셔닝(Partitioning)을 적용하거나 RDS를 복제하여 읽기 전용 DB로 사용하는 방법도 고려해 볼 만합니다. 데이터 분산을 통해 각각의 DB에 매우 높은 성능을 요구하는 요청을 분배할 수 있습니다.
캐싱 시스템 강화:
- 로그인 사용자에게도 캐싱 성능 향상을 시도해볼 수 있습니다. 사용자별 중요한 데이터는 적절한 TTL(Time To Live)과 일관성 정책을 설정해 캐싱하면 좋습니다. Redis의 활용을 검토해 보시기 바랍니다.
- 로그인 사용자에게도 캐싱 성능 향상을 시도해볼 수 있습니다. 사용자별 중요한 데이터는 적절한 TTL(Time To Live)과 일관성 정책을 설정해 캐싱하면 좋습니다. Redis의 활용을 검토해 보시기 바랍니다.
지표 기반 개선:
- 현재 성능 개선 수치와 사용량을 계속해서 모니터링하세요. 성능이 저하되는 페이지나 기능을 빠르게 인식할 수 있는 지표 체계를 구축하여 실시간으로 대응할 필요가 있습니다.
- 현재 성능 개선 수치와 사용량을 계속해서 모니터링하세요. 성능이 저하되는 페이지나 기능을 빠르게 인식할 수 있는 지표 체계를 구축하여 실시간으로 대응할 필요가 있습니다.
기타 제안:
- 더욱 강력한 쿼리 최적화를 고려하고, 필요한 경우 인덱싱을 재검토하세요. Django ORM과 같은 고성능 데이터베이스 도구를 사용하여 쿼리 성능을 최대한 향상시킬 수 있습니다.
- 더욱 강력한 쿼리 최적화를 고려하고, 필요한 경우 인덱싱을 재검토하세요. Django ORM과 같은 고성능 데이터베이스 도구를 사용하여 쿼리 성능을 최대한 향상시킬 수 있습니다.
대형 커뮤니티의 Pagination 최적화 사례
대형 커뮤니티들은 보통 데이터베이스 상의 구조를 최적화하고, 고성능 캐싱 시스템을 사용하며, 확장 가능한 데이터베이스 디자인을 통해 성능을 최적화합니다. 대표적으로 데이터베이스 샤딩 및 레디스 캐시를 활용해 대규모 트래픽을 효율적으로 처리합니다.
관련된 질문과 답변이 있어 유사한 사례를 다룬 게시물을 참고하시길 추천드립니다:
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해 드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
Build 관련 문제 (테스트 관련 문제)
0
41
2
인덱스 관련 질문 있습니다.
0
65
2
비관적 락 구현 방식 문의 건
0
67
2
외부 api 처리 방안에 대하여 궁금한 점이 있습니다.
0
87
2
네임드 락 사용 시 커넥션 풀을 분리하는 방법에 대한 질문
0
79
2
이벤트) 백엔드 기술면접 실전문제집
0
100
2
로컬에서 테스트 한 결과를 이력서에 써도 괜찮을까요?
0
128
2
데드락 발생 시, 외래 키를 제거하는 방법 관련 질문
0
83
2
Redis 캐싱을 도입하는데 db조회와 성능이 차이가 거의 없습니다.
0
109
2
k6 부하테스트 중인데 개선 전 성능이 너무 안나와서 고민
0
120
2
강의와 성능수치 비교
0
101
2
13강 강의 뒷부분의 과제 안내부분은 어디있나요?
0
50
2
이벤트 참가자 수 증가 후, save 메서드 호출 코드 질문
0
64
2
[수업 자료 질문] Cache Aside의 특징 문의
0
83
2
[수업자료 문의] RedisTemplate으로 SETNX 시 리턴값 문의
0
79
2
블로그에 학습 내용 정리 포스트를 올려도괜찮나요?
0
108
2
멀티스레드 상황인데 currentParticipants 가 AtomicInteger가 아닌 이유?
0
86
3
클라우드 환경 배포시 부하 테스트 방식에 대하여
0
136
2
k6 dashboad 안나오는 상
0
111
2
2-4 도커 빌드 에러가 계속 납니다.
0
296
2
AWS EC2에 도커 컨테이너가 동작하지 않을 때 확인 해야하는 것
0
113
2
성능 측정시
0
109
2
API 별 실행 쿼리 모니터링 구현 질문 있습니다.
0
83
2
이력서 작성에 대한 질문
0
110
2





