inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

6주 완성! 백엔드 이력서 차별화 전략 4가지 - 똑같은 이력서 속에서 돋보이는 법

조회속도 개선에서 더 개선하는 방법이 궁금합니다.

해결된 질문

21

문영훈

작성한 질문수 5

0

안녕하세요. 인덱스를 활용한 조회 성능 개선을 공부하던 중 궁금한 점이 생겨 질문드립니다.

현재 저는 OFFSET 기반 pagination을 사용하는 서비스를 개발하고 있으며, 다음과 같은 환경에서 성능 테스트를 진행했습니다.


1. 문제 상황

초기에는 OFFSET 제한 없이 마지막 페이지까지 이동 가능하도록 구현했습니다.
하지만 데이터가 1,000만 건 수준으로 증가하자, 깊은 페이지로 갈수록 조회 속도가 급격히 느려지는 문제를 확인했습니다.


2. 고민 및 제약

일반적으로 이 문제는 Keyset Pagination(커서 기반)으로 해결하라고 많이 알려져 있습니다.

하지만 제 서비스는
👉네비게이션 바를 통한 페이지 직접 이동 (ex. 1, 10, 100 페이지 클릭)
이 필요하기 때문에 Keyset 방식만으로는 요구사항을 만족시키기 어렵다고 판단했습니다.


3. 적용한 개선 방법

다음과 같은 방식으로 성능 개선을 진행했습니다.


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. 질문

  1. OFFSET 기반 pagination을 유지하면서
    👉추가로 성능을 개선할 수 있는 방법이 있을까요?

  2. 다음과 같은 방법들을 고려했는데, 방향성이 맞는지 궁금합니다.

    • RDS를 2개를 사용하여 조회 성능 데이터를 각각 2개의 db가 처리하도록 한다?

    • Keyset + OFFSET 혼합 방식
      (

      일반적인 페이지 이동은 Keyset Pagination을 사용하고,
      사용자가 특정 페이지를 직접 입력하거나 점프하는 경우에만
      제한적으로 OFFSET 기반 조회를 사용하는 혼합 방식)

    • RDS 스펙 업그레이드

  3. 또한 에펨코리아(https://www.fmkorea.com/)와 같은 대형 커뮤니티는 제가 원하는 페이지 네이션 방식을 사용하면서 깊은페이지(최대 1만)도 지원하고

    • 동시접속자 수십만

    • 페이지 수천~수만

    • 대량 데이터
      환경에서도 빠른 조회 성능을 유지하는데

    👉이러한 서비스들은 어떤 방식으로 pagination 및 조회 성능을 처리하는지 궁금합니다.

java spring mysql redis 이력서

답변 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 STATUSinnodb_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 인턴

안녕하세요, 인프런 AI 인턴입니다. 문영훈님.

인덱스를 활용한 pagination 성능 개선에 대해 심도 있는 질문을 주셨네요. OFFSET 기반 pagination에서 성능을 더욱 향상시키는 방법을 고민하고 계신데요, 질문하신 내용을 토대로 몇 가지 방법과 개선 방향을 제안드리겠습니다.

OFFSET 기반 Pagination 성능 개선 방안

  1. Keyset Pagination 혼합 사용:

    • 이미 제안하신 Keyset + OFFSET 결합 방식은 매우 유용할 수 있습니다. 일반적으로 Keyset 방식이 성능이 뛰어나고, OFFSET은 사용자 편의에 도움이 됩니다. 각 상황에 맞게 두 방식을 조합하면 성능과 편의성 간 균형을 맞출 수 있습니다.
  2. 데이터베이스 파티셔닝 및 복제:

    • 파티셔닝(Partitioning)을 적용하거나 RDS를 복제하여 읽기 전용 DB로 사용하는 방법도 고려해 볼 만합니다. 데이터 분산을 통해 각각의 DB에 매우 높은 성능을 요구하는 요청을 분배할 수 있습니다.
  3. 캐싱 시스템 강화:

    • 로그인 사용자에게도 캐싱 성능 향상을 시도해볼 수 있습니다. 사용자별 중요한 데이터는 적절한 TTL(Time To Live)과 일관성 정책을 설정해 캐싱하면 좋습니다. Redis의 활용을 검토해 보시기 바랍니다.
  4. 지표 기반 개선:

    • 현재 성능 개선 수치와 사용량을 계속해서 모니터링하세요. 성능이 저하되는 페이지나 기능을 빠르게 인식할 수 있는 지표 체계를 구축하여 실시간으로 대응할 필요가 있습니다.
  5. 기타 제안:

    • 더욱 강력한 쿼리 최적화를 고려하고, 필요한 경우 인덱싱을 재검토하세요. 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