V2의 DB 인프라 설계 - 100-hours-a-week/KTB4-3rd-CLOUD GitHub Wiki

V2 DB 인프라 설계와 MySQL 부하 실측

V1에서는 MySQL이 Spring Boot의 조회, 채팅 이력 저장, 좌석 승인과 같은 여러 경로의 상태를 저장한다. 조회량이 증가할 때 MySQL CPU나 디스크 I/O가 먼저 포화되는지, 쓰기 경합이 DB 처리 능력 또는 커넥션 점유 문제로 나타나는지 확인한다.

측정 범위

  • MySQL 컨테이너: 0.5 vCPU, 768 MB
  • 조회 경로: 목록·상세 DB 조회
  • 쓰기 경로: 채팅 메시지 저장, 동일 팟 좌석 승인
  • 관측 지표: MySQL CPU, HikariCP active·pending·acquire, lock 대기, commit p95, 조회 p95·p99

조회 부하 실측 결과

DB 조회를 포함한 detail 경로를 단계적으로 올린 결과는 다음과 같다.

목표 RPS 실제 RPS p95 오류율 MySQL CPU
50 45.9 13.2 ms 0% 0.036 cores
150 137.5 12.3 ms 0% 0.070 cores
300 274.5 16.7 ms 0% 0.105 cores

274.5 RPS에서 MySQL CPU는 배정량 0.5 vCPU의 21%였다. 단순 조회량이 MySQL CPU를 먼저 포화시키는 지점은 확인되지 않았다.

채팅 저장 부하 실측 결과

채팅 저장만 실행했을 때 실제 메시지율 47.4 msg/s, MySQL CPU 0.072 cores, HikariCP active 10/10, 대기 53건, 커넥션 획득 p95 445 ms, 커밋 p95 494 ms가 측정됐다. 저장과 조회를 동시에 실행하면 조회 p95 164.8 ms, p99 663.6 ms, 실패율 1.53%가 되었다.

MySQL CPU는 배정량의 14% 수준이었기 때문에 채팅 저장의 우선 병목은 DB CPU가 아니라 Spring에서 커넥션을 오래 점유하는 구조다.

인기 팟 경합 부하 측정의 참조 위치

픽스처 기반 좌석 경쟁과 실서버 동행 좌석·택시팟 생성 경합의 시나리오, 모든 상세 지표, 계측 한계 및 결과 해석은 V2 서비스 확장 가정 및 V1의 한계 지점의 **3절 ‘인기 팟 집중’**에 모았다. 이 문서에서는 해당 실측을 DB 선택지 검토의 입력으로 사용하고, DB topology별 성능·복구·운영비 trade-off와 추가 검증 항목을 정리한다.

V1 구조의 한계 지점 및 대안

단순 조회에서는 MySQL CPU 여유가 확인됐다. 채팅 저장과 좌석 승인에서는 MySQL CPU가 아니라 Spring·HikariCP의 커넥션 점유와 row lock 대기가 일반 조회로 전파됐다.

채팅 저장은 큐·배치 insert로 저장과 ACK 경로를 분리하고, 조회·쓰기용 HikariCP를 분리한다. 좌석 차감은 긴 row lock 트랜잭션을 줄이고 원자 카운터와 멱등키를 검토한다. 이 조치 후 rows examined, buffer pool hit, disk await, commit·fsync를 다시 측정해 MySQL 자체 확장 필요성을 판단한다.

인기 팟 정합성 부하로 본 데이터베이스 선택지와 트레이드오프

인기 팟 부하의 시나리오·계측·실측값과 단계별 해석은 V2 서비스 확장 가정 및 V1의 한계 지점의 3절 ‘인기 팟 집중’에 정리했다. 설계 선택에서는 정합성 검사가 통과한 점, 동행 좌석에서 lock 대기와 Hikari 점유가 늘어난 점, 택시팟 500 오류가 DB 자원 포화로 입증되지 않은 점을 기준으로 삼는다.

  • 실측 세부 지표와 단계별 해석은 한계 지점 문서의 인기 팟 절을 단일 기준으로 본다. 이 DB 문서는 같은 수치를 반복하지 않고 선택 논리와 추가 검증 기준을 다룬다.
  • 별도 detail 조회는 실제 274.5 RPS, 오류율 0%, p95 16.7 ms, MySQL CPU 0.105 cores(0.5 vCPU의 21%)였다. 이 조회 시험만으로는 DB CPU scale-up이나 read replica 필요성이 확인되지 않았다.

먼저 500의 원인이 된 애플리케이션 잠금·재시도·응답 매핑을 수정하고 같은 부하로 정합성과 가용성을 재검증한다. 부하 지표만으로 RDS 전환이나 DB 증설을 결론 내리지는 않는다. 아래 Single-AZ 선택은 측정된 성능 우위가 아니라 운영·백업을 관리형으로 분리하려는 요구와 Multi-AZ·복제본의 추가 비용을 함께 고려한 배치 결정이다.

대안별 트레이드오프

대안 해결하려는 문제 장점 비용·위험·한계 이 부하에서 확인할 지표
애플리케이션 잠금·오류 처리 개선 동일 좌표/팟 경쟁, 데드락, 500 응답 데이터베이스 종류와 무관하게 즉시 적용 가능. bounded retry, 멱등성, 일관된 잠금 순서, 조건부 갱신으로 실패 원인과 응답을 명확히 함 재시도 폭증은 경합을 악화시킬 수 있음. 고유 제약·트랜잭션 경계와 함께 설계해야 함 HTTP 500 → 의도된 409 MATCH_BUSY 전환, 가용성·p95/p99, 재시도 수, deadlock/lock wait, 정합성 검사
MySQL 컨테이너 유지·튜닝 현재 작은 부하와 비용 제약 현 구성 변경이 적고 비용이 낮음. 리소스와 MySQL 설정을 직접 통제 가능 백업·복구·패치·장애조치·모니터링을 팀이 직접 운영. DB와 호스트 장애 범위가 겹칠 수 있음 동일 부하에서 CPU·메모리·디스크 여유, 백업 성공/복구시간, DB 장애 복구시간, 월 운영비와 운영 투입
MySQL 컨테이너를 전용 호스트/스토리지로 분리 앱과 DB의 자원·장애 경계 결합 DB 자원을 독립적으로 할당하고 앱 배포와 DB 생명주기를 분리 가능 DB 자체 백업·복구·복제·패치·장애조치는 계속 직접 구성·운영 호스트 장애 영향, 볼륨 복구, RPO/RTO, 운영 toil, 컨테이너 공유 호스트 대비 지표
RDS for MySQL Single-AZ (선택) 관리 부담·백업 운영을 낮은 RDS topology 비용으로 확보 관리형 백업/PITR, 패치·모니터링 등 운영 기능 활용 가능 (AWS 백업·복구 문서) AZ 장애 자동 failover는 제공하지 않음. 컨테이너보다 별도 DB 인스턴스 비용과 AWS 종속성이 증가 컨테이너 대비 동일 부하 성능·월 TCO, PITR 복구시간·복구 시점, 운영 투입시간
RDS for MySQL Multi-AZ DB 인스턴스/AZ 장애 복구 관리형 standby와 failover를 제공해 가용성·복구 경계를 개선 (AWS Multi-AZ 문서) 단일 AZ보다 비용이 증가하고 failover 중 연결 재수립 처리가 필요함. standby는 읽기 트래픽 처리용이 아님 장애조치 시험 RTO, 복구 후 데이터 확인 RPO, 앱 재연결 시간·오류율, Multi-AZ 추가 비용
RDS Proxy 추가 태스크 확장 때 연결 생성 급증·DB 연결 상한 연결 풀링·재사용과 연결 폭주 완화 (AWS RDS Proxy 문서) 추가 비용과 구성 요소·관측 대상이 생김. SQL, row lock, 긴 트랜잭션과 비즈니스 오류는 해결하지 않음 DB connections/max, 신규 연결률, Hikari pending/acquire/timeout, Proxy 대기·차용 지연, 추가 비용
읽기 복제본 추가 읽기 부하가 writer의 CPU/IO를 제한 읽기를 writer에서 분산할 수 있음 (AWS read replica 문서) 비동기 복제 지연으로 최신 읽기가 필요한 요청에는 부적합. hot-row 쓰기 경합은 줄이지 않음 writer CPU/IO·조회 지연, 읽기 비중, replica lag, stale read 허용 범위, replica 비용
Aurora MySQL 검토 RDS for MySQL에서 입증된 성능·확장·복원력 한계 별도 관리형 DB 아키텍처 대안으로 성능·장애복구 요구를 비교 가능 구조와 운영 선택지가 늘고 비용도 다시 산정해야 함. 현재 측정만으로 우위가 입증되지 않음 같은 데이터·부하의 처리량, 꼬리 지연, 장애 복구, 스토리지 동작, 총비용 비교

최종 선택: RDS for MySQL Single-AZ

V2 초기 운영 DB는 Prod RDS for MySQL Single-AZ, 단일 writer, 읽기 복제본 없음으로 선택한다. Dev는 현재처럼 Dev EC2의 MySQL 컨테이너를 유지해 Prod RDS 비용이 개발 환경에 중복 발생하지 않도록 한다. 이 결정은 읽기 replica를 붙여 확장하는 설계가 아니라, DB 운영·백업을 관리형 서비스로 분리하면서 Multi-AZ의 추가 인스턴스 비용은 보류하는 단계적 선택이다.

트래픽 유형 근거

서비스 DB 부하는 보고서·분석형 대량 조회가 아니라 조회와 상태 변경이 섞인 트랜잭션형 OLTP다.

  • 탐색·상세 API는 일반 조회를 수행한다. 상세 DB 조회 경로는 실제 274.5 RPS에서 p95 16.7 ms, 오류율 0%, MySQL CPU 0.105 cores(할당 0.5 vCPU의 21%)였다. 현재 데이터·쿼리 조건에서 읽기 처리량이 DB CPU를 포화시키거나 read replica를 요구한다는 증거는 없다.
  • 채팅 저장은 메시지 INSERT와 마지막 메시지 포인터 UPDATE가 포함된 쓰기다. 47.4 msg/s에서 MySQL CPU는 0.072 cores(14%)였고, HikariCP active 10/10·pending 53·커넥션 획득 p95 445 ms가 관측됐다. 우선 병목은 DB CPU보다 긴 요청/트랜잭션에 따른 애플리케이션 커넥션 점유다.
  • 동행 참여는 코드상 방장 승인 대기 요청이 아니라 참여 API가 즉시 좌석을 확보하는 흐름이다. 정원 조건을 포함한 current_count < capacity 갱신과 참여자 고유 제약으로 동시 요청을 처리한다. 같은 팟의 단일 행 갱신 때문에 row lock 대기와 Hikari pending이 발생했지만 MySQL CPU는 최대 0.09 cores(18%)였고 정합성 검사는 통과했다. 읽기 replica는 writer의 같은 행 쓰기 직렬화나 잠금 대기를 줄이지 않는다.
  • 택시팟 생성의 HTTP 500은 잠금 충돌을 의도된 MATCH_BUSY로 변환하지 못한 애플리케이션 응답 경로가 우선 원인 후보이며, 해당 실행에는 MySQL CPU 측정값도 없다. 이를 근거로 DB 인스턴스를 늘리거나 복제본을 추가하지 않는다.

따라서 현재 유형은 읽기 확장이 시급한 read-heavy 부하가 아니라, 조회와 쓰기가 함께 있고 일부 쓰기 경로에 hot-row 경합이 있는 단일 writer OLTP로 판단한다. 우선 SQL·트랜잭션·잠금 범위와 애플리케이션 충돌 처리를 개선한다. 읽기 비중만으로 replica를 결정하지 않고, writer CPU/IO와 읽기 SLO가 실제로 악화되는지 다시 측정한다.

인프라 비용 근거

비용은 서울 리전, On-Demand, 월 730시간의 설계 추정치이며 실제 과금은 인스턴스 클래스·스토리지·백업 사용량·데이터 전송 및 CPU credit에 따라 달라진다. 기존 비용 검토에 사용한 db.t4g.small Single-AZ 단가 가정 $0.051/시간을 적용하면 DB 컴퓨팅은 약 $37.23/월이다. 이 값은 컴퓨팅만 계산한 예시로, 스토리지·백업 초과분·모니터링·데이터 전송 등은 포함하지 않는다. db.t4g.small은 비용 추정을 위한 시작 클래스이며, 0.5 vCPU·768 MB로 제한한 MySQL 컨테이너 부하 결과가 해당 RDS 클래스의 성능을 직접 입증하지는 않는다. 배포 전 동일 시나리오로 후보 클래스를 검증하고 AWS Pricing Calculator 또는 RDS for MySQL 가격표에서 해당 리전·클래스로 다시 산정한다.

토폴로지 DB 인스턴스 과금 기준 현재 선택에 대한 판단
Prod EC2의 MySQL 컨테이너 DB 전용 인스턴스 시간은 추가되지 않음. 다만 EC2/EBS 용량, 백업 저장소, 호스트 자원 점유와 직접 운영 비용은 존재 월 현금 비용은 낮지만 앱·DB가 호스트 장애와 자원 경합을 공유하고 백업·복구·패치를 직접 관리해야 함
RDS Single-AZ db.t4g.small 1대의 인스턴스 시간 + 할당 스토리지 + 백업 저장소/기타 사용량 선택. 관리형 백업·복구와 DB 생명주기 분리를 확보하며 DB 인스턴스 비용은 1대분으로 제한
RDS Multi-AZ DB 인스턴스 Primary와 Standby 모두의 인스턴스 시간 및 스토리지 비용 AWS 과금 설명상 Single-AZ보다 대략 2배 수준이 될 수 있음. 현재는 AZ 자동 failover에 그 추가 비용을 배정하지 않음. Standby는 읽기 처리에 사용할 수 없음 (AWS 과금 기준)
RDS Single-AZ + read replica Primary와 replica 인스턴스 시간·각 스토리지 비용 읽기 확장 증거가 없어 두 번째 DB 인스턴스 비용을 보류. MySQL replica는 추가 DB 인스턴스로 과금되며 복제가 비동기임 (AWS replica 과금·복제)
RDS Multi-AZ DB cluster Writer 1대 + 읽기 가능한 Reader 2대 등 3개 DB 인스턴스 운영 가용성과 읽기 확장을 함께 사는 더 높은 비용 구조. 현재 부하에는 선택 근거가 부족함 (AWS 클러스터 토폴로지)

Single-AZ는 AZ 장애 시 자동 standby 전환을 제공하지 않는다는 가용성 위험을 수용한다. 그 대신 Multi-AZ의 상시 standby 비용을 피하고, 자동 백업/PITR 및 주기적인 복구 리허설로 복구 절차를 검증한다. 제품의 복구 목표를 시험해 충족하지 못하거나 단일 AZ 장애가 허용 불가능한 서비스 영향으로 판명되면, 그때 Multi-AZ의 추가 월 비용과 자동 failover 이점을 다시 비교한다.

⚠️ **GitHub.com Fallback** ⚠️