te___ho
NO RULES
te___ho
전체 방문자
오늘
어제
  • 분류 전체보기 (92)
    • 주니어의 개발일지 (1)
    • My project (29)
      • High Traffic Lab (5)
      • Nanaland in Jeju (8)
      • Univey (3)
      • inflearn_clone? (13)
    • Spring (19)
    • Network & CS (9)
    • Java (1)
    • Front_End (8)
    • Algorithm (11)
    • ETC (6)
    • Scribble (8)

인기 글

최근 글

티스토리

hELLO · Designed By 정상우.
te___ho

NO RULES

6000만개 데이터 갖고 조회하며 충격받기
My project/High Traffic Lab

6000만개 데이터 갖고 조회하며 충격받기

2025. 3. 19. 17:05
 

데이터. 몇개까지 INSERT 해봤나요? (Feat. 대용량 데이터, 데이터 6000만개 INSERT 해보기, Bulk INSERT, Mul

대규모 트래픽, 대용량 데이터 처리에 대한 경험을 쌓기 위해 진행한 개인 프로젝트의 시작은 우선 많은 데이터를 생성하는 것이었다. 시작부터 고민이 많았는데 "몇 개부터가 대용량이지?", "이

te-ho.tistory.com

저번 글에서 6000만개의 데이터를 INSERT 했으니 이번에는 이를 활용해서 조회를 해보자! 이 과정에서 깨달은 점이 많았다. 이래서 실무에서 많은 데이터/트래픽을 처리한 경험을 요구하는구나... 지금까지 내가 했던 것은 천국에서 모든 것이 가능한 환경에서 개발을 했던 것이었구나 싶었다..! 무슨 일이 있었는지 한번 되돌아보려 한다.

ERD 간략도

  • Users : 유저 정보를 저장 (id, name, level, age)
  • Address : 주소 정보를 저장(id, user_id(FK), city, street, zipcode)
  • Orders : 발생한 주문에 대한 정보 저장 (id, user_id(FK), order_date)
  • Payments : 주문에 대한 결제 정보 저장 (id, order_id(FK), status, payment_date)
  • Items : 상품 정보 저장 (id, category_id(FK), price, name)
  • OrderItems : Items, Orders 다대다 관계 연결 테이블 (id, item_id(FK), order_id(FK), quantity)
  • Category : 상품 카테고리 정보 저장 (id, name)
여기서 특이 사항은 ERD에서 연관 관계로 작성한 부분들에서 join에 필요한 키들을 외래 키로 관리하지 않고 일반 필드로 사용하였다.

그 이유는 실무에서는 유연성 등의 이유로 FK를 필수로 사용하지 않을 수도 있다는 점을 배운 적이 있고, 이후에 인덱스를 직접 적용하기 위해서이다. (@ManyToOne,, 등 연관관계 설정하면 Hibernate가 자동으로 인덱스를 생성하기 때문에!!!)
전체적인 구상은 예제에서 많이 사용되는 주문 도메인에서 생길 수 있는 테이블들을 만들었다. 물론 Users - Address와 같이 불필요한 관계가 보일 수 있다. 하지만 여러 Join 관계를 통해서 성능 비교를 극대화하기 위해서 조금 조잡한 부분이 생긴 것이니 너그럽게 넘어가 주세요!

조회를 해보자

몇천만 개의 데이터를 다루는 것이 처음이니 우선 간단하게 id로 조회를 해보았다. 3개의 join을 통해 Order에 대한 통합 정보를 조회하는 쿼리를 사용해 조회를 진행했다.

그 결과는 매우 충격적이었는데...

조회 시간 3.54초...

단순하게 id로 단일 데이터 결과값을 요청했는데 3초가 넘어갔다. 3540ms... 지금까지 볼 수 없었던 시간이었고 충격적이어서 orderId를 랜덤하게 100번 요청하는 실험을 Jmeter로 진행했다.

100번의 조회 요청 시 timed out 에러 발생

역시 어림도 없었고 평균 속도 25879ms,,,, 심하게 오래 걸린 이유는 이후에 timed out 에러가 발생했기 때문이다. 약 6천만개 데이터에서 100번의 요청을 견뎌내지 못했다. 
면접을 보면서 실무 상황에서 간단하게 들었을 때 하루에 몇천만 개의 데이터가 새로 생긴다 말씀해 주셨었다. 그렇다면 전체 데이터의 수는 훨씬.. 훨씬 더 많을 것이고 요청도 100번은 우습게 넘어갈 것이다. 일단 100번의 조회를 통과하기 위해 대책이 필요해 보였다.

Index 처리

사실 면접을 볼 때 조회 속도 개선에 대한 질문을 받으면 Index를 통해 해결할 수 있다!라는 대답을 여러 번하였지만 정말 필요해서 인덱스를 설정해 본 적은 한 번도 없었다. 이번에는 정말 조회 속도가 오래 걸리기 때문에 index를 설정해 보았다. PK를 사용하지 않고 join 하는 OrderItem에 인덱스를 설정해 준 후 같은 쿼리를 실행해 보았다.

한번 조회 - 18ms 소요
100번 동시 조회 - 평균 14ms

인덱스를 적용하니 단일 조회 시 3540ms -> 18ms로 약 99% 감소되었고, 터져버렸던 100번의 동시 조회 또한 평균 14ms 속도로 성공했다. 인덱스를 생성, 삭제하는 문법을 알아보기 위해 사용해 본 적은 있었지만 이렇게 사용 효과를 직접 경험해 보니 중요성을 체감할 수 있었다.

인덱스 생성하면 끝일까?

인덱스의 성능에 신나서(?) 이것저것 많은 필드에 인덱스를 걸어보면서 신기해하고 있었는데 역시 한 번에 수월하게 되면 개발이 아니다..

음...? 왜 시간이 드라마틱하게 안줄어들지...?

User 테이블에 사용자 등급을 나타내는 level 필드에 인덱스를 걸고 조회를 해보았는데 성능 개선이 안 되는 듯 보였고 심지어 어떨 때는 인덱스를 걸었을 때 더 오랜 시간이 걸리기도 했다. 인덱스 생성이 안되었는지 확인해 보고, 인덱스를 재생성해 보아도 속도가 빨라지지 않아서 실행 계획을 확인해 보았다.

key가 null이다....

인덱스에 활용할 key는 분명 등록되어 있었지만 key가 null이다. 그 뜻은 InnoDB의 옵티마이저가 인덱스를 사용하지 않고 조회를 하고 있다는 것이었다. 이에 대해 공부를 해보니 조회할 데이터의 양이 작을 경우 옵티마이저가 full scan이 더 효율적이라고 판단한 것이었다. 일정 비율의 값이 존재하지 않으면 인덱스를 사용하지 않는 것이 더 효율적이라는 것을 알게 되었다.

1천만 개의 데이터, 존재하는 level은 5개

내 경우에도 천만 개의 데이터가 1~5의 레벨 중 1개, 즉 5개의 level 분포만 갖고 있기 때문에 인덱스를 굳이 사용하지 않는 것이다.(level의 범위를 더 확장했을 때는 index가 적용되었다.) 이에 대한 성능 개선을 하고 싶은 경우에 레벨이 고르게 분포되어 있다면 파티셔닝을 적용하는 대안이 있을 것 같다.
(만약 이 글을 보고 공부를 하시는 분이라면 인덱스를 사용할 수 없는 경우, full scan이 더 효율적인 상황에 대해 공부해 보시면 좋습니다!)

마무리

데이터를 INSERT 할 때도 신기했는데 이를 다루다 보니 더 신기한 경험을 많이 했다. 글에 남기지는 않았지만 정말 많은 인덱스, 복합 인덱스를 만들어서 조회해 보았고 실행계획도 하나하나 다 확인해 보았다.
공부를 하다보니 지금까지 프로젝트를 진행하면서 사용했던 pagination, 무한 스크롤 기능이 예뻐보이기 위해서가 아니라 대용량 데이터를 효율적이게 처리하기 위함이었구나.. 내가 했던 프로젝트의 상황에서는 redis, pagination, 무한 스크롤 구현을 그냥 DB에 쿼리 날려서 전체 조회 했어도 전혀 상관이 없었구나..를 알게 되었다. (...이 많지만 알게 되어서 기분 좋았다. 정말이다.) 
실무에서는 내가 해보지 못한 고민을 많이 해야겠구나 싶어서 혼자 꼬리의 꼬리 질문을 만들어가며 많은 공부를 할 수 있었다. "redis를 사용하는데 redis의 저장 공간이 부족하면??", "센티널 적용했는데 마스터의 공간이 부족하면??", "WAS 자체를 클러스터링이 필요하다면?" 등등 머리가 터질 뻔했지만 내 인텔리제이, gpt, 구글을 실컷 괴롭히며 알아가는 시간을 갖었다.
여기서 끝! 을 하기에는 뭔가 부족하다 싶어서 하나의 상황을 더 가정했다. 읽기뿐만 아니라 쓰기 작업도 무수히 많다면 Index가 최종 해결책이 아닐 수 있겠다는 생각을 하게 되었고 아키텍처 구조를 과감하게 확장해 보았다. 이에 대한 내용은 다음 시간에~

728x90
반응형
저작자표시 (새창열림)

'My project > High Traffic Lab' 카테고리의 다른 글

CQRS와 함께한 고군분투(Feat. 개발자스럽게(?) 생각하려 노력하기)  (2) 2025.08.21
MySQL Index 사용 시, 쓰기 성능이 정말 저하되나?? (Feat. CSV로 빠르게 데이터 넣기)  (2) 2025.07.02
데이터. 몇개까지 INSERT 해봤나요? (Feat. 대용량 데이터, 데이터 6000만개 INSERT 해보기, Bulk INSERT, Multi-Value Insert)  (0) 2025.02.23
주니어 개발자가 대규모 트래픽, 대용량 데이터 경험하는 방법(Feat. 뇌피셜)  (1) 2024.12.16

    티스토리툴바