이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • DB는 어떤 자원에서 한계에 부딪히는가?
  • 서버를 늘렸는데 왜 DB가 먼저 터질 수 있는가?
  • 백엔드 개발자는 DB 비용을 어떤 언어로 설명해야 하는가?

개요

DB 한계는 CPU 하나로 설명되지 않는다. 연결 수, I/O, 메모리, lock, log, replication, storage, network가 모두 한계가 될 수 있다. 애플리케이션 서버는 쉽게 늘릴 수 있지만, DB는 공유 상태를 가진 시스템이라 수평 확장이 어렵다.

백엔드 개발자는 기능 개발 시 DB 비용을 예상하고, 장애 시 어떤 자원이 병목인지 분리해야 한다.

원리

DB 비용은 “쿼리 한 번이 몇 ms인가”만으로 판단할 수 없다. 같은 30ms 쿼리라도 초당 1번 호출되면 문제가 작고, 초당 2,000번 호출되면 DB 전체를 밀어낼 수 있다. 반대로 2초짜리 쿼리라도 관리자 기능에서 하루 한 번 replica에서 실행하면 허용될 수 있다.

백엔드 개발자는 단일 실행 시간, 호출 빈도, 동시성, 읽은 row 수, 정렬/조인 비용, lock 대기, 결과 크기, 로그 생성량을 함께 본다. 이 조합이 DB의 CPU, I/O, memory, connection, lock, network, replication 한계 중 어디를 압박하는지 설명할 수 있어야 한다.

주요 한계

DB에서 자주 만나는 한계는 다음이다.

  • CPU: 조인, 정렬, 집계, 함수 계산
  • I/O: 테이블 스캔, 인덱스 랜덤 접근, checkpoint
  • Memory: buffer cache, sort memory, connection memory
  • Connection: max connections, pool 고갈
  • Lock: row/table/gap/metadata lock
  • Log: WAL/binlog 증가
  • Replication: replica lag
  • Storage: bloat, index size, backup size
  • Network: DB 왕복 횟수, 큰 result set, cross-region latency

느린 API 하나도 이 중 여러 원인이 섞일 수 있다.

서버 확장과 DB 병목

애플리케이션 인스턴스를 2대에서 10대로 늘리면 DB로 들어가는 동시 쿼리와 커넥션도 늘어난다.

pool 10 x 2대 = 최대 20 connections
pool 10 x 10대 = 최대 100 connections

웹 서버 CPU는 여유가 생겨도 DB는 더 많은 동시 쿼리를 처리해야 한다. 느린 쿼리나 lock 경합이 있으면 확장은 오히려 장애를 키울 수 있다.

비용 모델

SQL 비용은 대략 다음 요소로 생각한다.

  • 몇 행을 읽는가?
  • 어떤 인덱스를 쓰는가?
  • 정렬이나 해시 집계가 필요한가?
  • 조인 중간 결과가 얼마나 큰가?
  • 락을 얼마나 기다리는가?
  • 결과를 얼마나 많이 전송하는가?
  • 얼마나 자주 호출되는가?

백엔드 리뷰에서 “이 쿼리는 느릴 것 같아요”보다 “이 쿼리는 status 조건이 낮은 선택도이고, created_at 정렬 인덱스가 없어 많은 row sort가 발생할 수 있습니다”라고 말해야 한다.

증상과 원인을 연결할 때는 다음처럼 가설을 세운다.

증상먼저 의심할 것
DB CPU가 높다full scan, 비싼 join, sort, aggregate, 함수 계산
CPU는 낮은데 latency가 높다lock wait, I/O wait, network, pool 대기
pool pending이 증가한다slow query, 긴 transaction, pool 과소/과대 설정, DB max connections
replica lag가 증가한다write spike, 대량 DML, index 추가, checkpoint/I/O 부하
특정 row에서만 지연된다hot row, 순번 채번, 재고/포인트 같은 집중 update

이 표는 정답표가 아니라 대화의 시작점이다. 인프라/DBA가 보는 지표와 백엔드가 아는 기능 맥락을 합쳐야 원인이 좁혀진다.

실전 팁

  • API별 DB 호출 수와 호출 빈도를 본다.
  • p95/p99 지연은 평균보다 중요하다.
  • 서버 확장 전 DB 총 연결 수와 slow query를 점검한다.
  • 대형 기능 출시 전 데이터 증가량과 인덱스를 예상한다.
  • DB 비용은 인프라/DBA와 공유 가능한 지표로 말한다.
  • “단일 쿼리 시간 x 호출 빈도 x 동시성”으로 기능의 DB 예산을 거칠게라도 계산한다.
  • 장애 중에는 tuning보다 먼저 트래픽 제한, batch 중단, timeout 조정, 읽기 경로 차단처럼 피해 확산을 줄일 선택지를 검토한다.

위험 신호!

  • DB CPU만 보고 lock wait를 놓친다.
  • 서버를 늘리면 DB도 자동으로 버틸 것이라 생각한다.
  • connection 수를 계산하지 않고 오토스케일링한다.
  • 쿼리 호출 빈도를 보지 않고 평균 실행 시간만 본다.
  • replica lag를 읽기 확장의 당연한 부작용으로만 방치한다.
  • 애플리케이션 서버 scale-out을 DB 연결 총량 증가로 환산하지 않는다.
  • lock wait를 DB 성능 문제로만 보고 긴 transaction을 만든 코드 경로를 추적하지 않는다.

확인 질문

  • DB 한계를 CPU만으로 설명하면 부족한 이유는 무엇인가?
    • DB는 연결 수, I/O, lock, memory, log, replication, storage 등 다양한 자원에서 병목이 생기기 때문이다.
  • 애플리케이션 서버 확장이 DB 장애를 키울 수 있는 이유는 무엇인가?
    • 서버 수가 늘면 DB로 들어가는 동시 연결과 쿼리도 늘어 공유 병목을 더 세게 압박하기 때문이다.
  • SQL 비용을 설명할 때 봐야 할 요소는 무엇인가?
    • 읽는 행 수, 인덱스, 정렬/집계, 조인 중간 결과, 락 대기, 결과 크기, 호출 빈도다.
  • 평균 쿼리 시간이 낮아도 운영 장애가 날 수 있는 이유는 무엇인가?
    • 호출 빈도와 동시성이 높거나 p99 지연, lock wait, pool 대기가 커지면 평균은 낮아도 사용자 요청과 DB 전체가 밀릴 수 있기 때문이다.

참고 문서