DB 인덱스 성능 최적화 1000만 건 데이터도 0.1초 만에 조회하는 15년차 백엔드 개발자의 실전 비법
- 공유 링크 만들기
- X
- 이메일
- 기타 앱
DB 인덱스 성능 최적화 1000만 건 데이터도 0.1초 만에 조회하는 15년차 백엔드 개발자의 실전 비법
📑 목차
🐢 쿼리 속도가 느리신가요? DB 인덱스와 성능 최적화의 모든 것
안녕하세요, 여러분. 15년 차 백엔드 개발자로서 솔직히 고백하자면, 저도 주니어 시절엔 데이터베이스(DB)를 그저 '데이터를 영구 저장하는 마법 상자' 정도로만 생각했습니다. 로컬 개발 환경에서 데이터 100건 정도 넣고 테스트할 때는 모든 게 완벽했죠. 쿼리 실행 시간은 항상 0.01초 미만이었으니까요. 그런데 실무에 투입되고 사용자 데이터가 100만 건, 1,000만 건을 넘어가면서 악몽이 시작되었습니다. 게시판 목록 하나 띄우는데 5초가 걸리고, 새벽 배치 작업이 아침 9시 출근 시간까지 안 끝나는 상황... 혹시 경험해 보셨나요? 💦
많은 개발자분들이 새로운 언어 문법이나 프레임워크 기능은 열심히 공부하지만, 정작 시스템의 병목이 가장 많이 발생하는 '데이터베이스 최적화'는 소홀히 하는 경향이 있습니다. "그냥 느리면 인덱스 걸면 되는 거 아니야?"라고 단순하게 생각하신다면, 오늘 이 글이 여러분의 퇴근 시간을 획기적으로 당겨줄 겁니다. 오늘은 DB가 데이터를 찾는 근본적인 원리부터 실행 계획(Explain) 분석, 그리고 인덱스를 탔는데도 느린 이유와 해결책까지 아주 깊게 파고들어 보겠습니다. 커피 한 잔 딱 준비하시고, 시작해볼까요? ☕
🔍 인덱스(Index), 도대체 내부에서 무슨 일이 일어나는가?
인덱스를 이해하려면 먼저 'Full Table Scan(풀 테이블 스캔)'의 공포를 이해해야 합니다. 도서관에 갔다고 상상해 보세요. 50만 권의 책이 있는데, 책들이 순서 없이 무작위로 꽂혀 있습니다. 여기서 '클린 코드'라는 책을 찾으려면 어떻게 해야 할까요? 네, 첫 번째 책장부터 마지막 책장까지 책 하나하나를 다 꺼내서 제목을 확인해야 합니다. 이게 바로 인덱스가 없을 때 DB가 하는 일입니다.
데이터베이스에서 디스크 I/O(입출력)는 메모리 접근보다 약 10만 배 이상 느립니다. 풀 테이블 스캔은 디스크의 모든 데이터 블록을 메모리로 퍼 올리는 작업이므로, 데이터가 늘어날수록 성능은 선형적으로, 아니 때로는 지수적으로 나빠집니다. 제가 예전에 맡았던 이커머스 프로젝트에서 주문 내역 조회 쿼리가 타임아웃(30초)이 나서 전체 서비스가 마비될 뻔한 적이 있는데, 알고 보니 `order_date` 컬럼에 인덱스가 없어서 5,000만 건의 주문 데이터를 매번 처음부터 끝까지 뒤지고 있었던 거죠.
💡 B-Tree 구조의 마법과 원리
대부분의 RDBMS(MySQL, PostgreSQL, Oracle 등)는 인덱스 자료구조로 B-Tree(Balanced Tree)를 사용합니다. 데이터베이스는 단순히 "값이 10인 것"만 찾는 게 아니라, "10보다 크고 20보다 작은 것" 같은 범위 검색(Range Scan)을 아주 빈번하게 수행하기 때문입니다. B-Tree는 데이터가 항상 정렬된 상태를 유지하며, 루트 노드에서 리프 노드까지의 거리가 일정합니다. 1,000만 건의 데이터가 있어도 B-Tree의 깊이(Depth)는 보통 3~4단계 정도에 불과합니다. 즉, 1,000만 번 비교해야 할 작업을 단 3~4번의 디스크 읽기로 끝낼 수 있다는 뜻입니다.
📊 한눈에 보는 비교: 풀 스캔 vs 인덱스 스캔
이론만으로는 와닿지 않을 수 있습니다. 실제 100만 건의 회원 테이블에서 특정 회원을 검색할 때의 차이를 표로 정리했습니다.
| 비교 항목 | Full Table Scan (인덱스 X) | Index Range Scan (인덱스 O) |
|---|---|---|
| 시간 복잡도 | O(N) - 데이터 전체 비례 | O(log N) - 획기적 단축 |
| 디스크 I/O | 모든 페이지 읽기 (수만 block) | 필요한 페이지만 읽기 (3~5 block) |
| 응답 속도 (예시) | 약 3.5초 ~ 10초 이상 | 약 0.01초 ~ 0.05초 |
| CPU 부하 | 매우 높음 (모든 행 필터링) | 매우 낮음 |
⚠️ 주의할 점: 인덱스는 공짜가 아닙니다!
인덱스는 별도의 저장 공간(보통 데이터 크기의 10~30%)을 차지하며, 데이터가 삽입(INSERT), 수정(UPDATE), 삭제(DELETE)될 때마다 인덱스도 함께 재정렬해야 합니다. 즉, 읽기 속도를 얻는 대신 쓰기 속도를 희생하는 트레이드오프(Trade-off)가 발생합니다. 제가 본 최악의 케이스는 테이블 하나에 인덱스를 15개나 걸어놔서, 데이터 한 건 넣을 때마다 15번의 인덱스 업데이트가 발생해 서버가 멈추는 경우였습니다. 보통 한 테이블당 인덱스는 3~5개가 적당합니다.
📉 인덱스 트러블슈팅: 인덱스를 걸었는데 왜 느릴까?
"선배님, 저 인덱스 걸었는데 쿼리가 여전히 느려요." 주니어 개발자들이 저에게 가장 많이 물어보는 질문 중 하나입니다. 인덱스가 있다고 해서 DB가 무조건 사용하는 것은 아닙니다. DB 옵티마이저(Optimizer)는 매우 똑똑하지만, 우리가 짠 쿼리가 엉망이면 인덱스를 포기하고 풀 스캔을 선택하기도 합니다. 대표적인 '인덱스를 타지 않는' 안티 패턴 3가지를 살펴봅시다.
💬 여러분의 경험을 들려주세요!
✨ 이 방법을 시도해보셨나요? 댓글로 공유해주세요!
📌 도움이 되셨다면 저장하고 주변에도 알려주세요.
🔔 더 많은 개발 팁을 받고 싶다면 구독해주세요!
이 글이 도움되셨나요? 공유해주세요!
아래 링크를 통해 구매 시 운영자에게 일정 수수료가 발생할 수 있습니다.
'제시된 기존 키워드(도커/쿠버네티스 트러블슈팅, AWS 비용, 와이파이 하드웨어, 갤럭시 모바일, 챗GPT 업무 활용)와 주제가 겹치지 않는, **개발 언어, 데이터베이스, 웹 보안, 버전 관리** 분야의 새로운 검색 키워드 4개입니다.' 관련 상품을 쿠팡에서 확인해 보세요.
상품 보러가기 →- 공유 링크 만들기
- X
- 이메일
- 기타 앱
댓글
댓글 쓰기