RDS 기본기와 운영에서 터지는 지점
RDS는 백업, 패치, 장애 전환을 관리형으로 맡기는 데이터베이스 서비스이지만, Multi-AZ, Read Replica, Connection Pool, RDS Proxy의 경계를 이해해야 운영 장애를 줄일 수 있다. 특히 DNS 캐시, 복제 지연, 연결 수 초과, 네트워크 접근 조건처럼 에러가 명확하지 않거나 환경별로 다르게 보이는 지점을 중심으로 살펴본다.
Script Companion
오디오와 함께 스크립트 보기
- 01
RDS는 EC2에 직접 데이터베이스를 설치해서 운영하는 부담을 줄이기 위한 관리형 DB 서비스다. 대부분의 서비스는 데이터를 DB에 저장하므로, 백업, 패치, 스케일링, 장애 전환을 누가 책임지는지가 운영 난이도를 크게 바꾼다. RDS는 MySQL, PostgreSQL, MariaDB, Oracle, SQL Server, Aurora를 지원하고, 인스턴스 클래스는 CPU와 메모리 성능을 정하는 단위다. 작은 db.t3.micro부터 큰 db.r6g.16xlarge까지 선택할 수 있지만, 어떤 클래스를 쓰든 RDS는 반드시 Private Subnet에 두고 같은 VPC 안의 애플리케이션만 Security Group으로 접근하게 하는 것이 기본 보안 원칙이다.
- 02
Multi-AZ는 장애 대비를 위한 구조다. 평소에는 Primary가 읽기와 쓰기를 처리하고, 다른 가용영역에 있는 Standby가 예비 역할을 한다. 모든 쓰기 작업은 Primary에서 Standby로 동기식 복제되고, Primary 장애가 감지되면 RDS가 DNS 레코드를 Standby의 IP로 바꾼다. 그래서 애플리케이션은 같은 Endpoint 주소를 계속 쓰고, 재연결만 하면 된다. 일반적인 Multi-AZ DB Instance는 장애 감지부터 standby에서 트랜잭션 재개까지 60초에서 120초가 걸릴 수 있고, Multi-AZ DB Cluster는 일반적으로 35초 미만이다. 다만 클라이언트가 체감하는 시간에는 DNS 캐시 만료와 드라이버 재연결 시간이 더해진다.
- 03
Failover에서 자주 놓치는 지점은 RDS 자체는 Available인데 애플리케이션만 계속 옛 Primary로 붙으려는 silent failure다. 일부 프로세스가 1분에서 5분 동안 옛 IP로 ECONNREFUSED를 반복하면, 로그는 있지만 Failover가 끝났으니 곧 회복될 것처럼 보일 수 있다. 원인은 JVM 또는 Sequelize와 pg 커넥션 풀의 DNS 캐시 TTL이 무한대인 -1로 남아 옛 Primary IP를 계속 들고 있는 경우다. AWS 공식 권장값은 TTL 60초 이하이고, Node.js는 dns.lookup 자체를 캐시하지 않더라도 ORM이나 풀이 keep-alive로 옛 TCP 연결을 유지하면 비슷한 증상이 난다. 이때는 idleTimeoutMillis 30000 이하로 재연결을 유도하거나, RDS Proxy로 이 책임을 옮길 수 있다.
- 04
Read Replica는 Multi-AZ와 목적이 다르다. Multi-AZ의 Standby는 장애 전환을 위한 예비 DB이고, Read Replica는 읽기 부하를 분산하기 위한 복제본이다. 쓰기는 Primary에서 처리하고 읽기는 별도 Endpoint를 가진 복제본에서 처리할 수 있으며, NestJS에서 TypeORM 설정으로 읽기와 쓰기 DB를 나눌 수 있다. 하지만 Read Replica는 비동기 복제라서 방금 Primary에 쓴 데이터가 복제본에서는 아직 보이지 않을 수 있다. 이 상태는 에러가 아니라 오래된 데이터 반환으로 나타나며, ReplicaLag가 0이 아닐 때 특히 주의해야 한다. 쓰기 직후 즉시 읽어야 하는 쿼리나 재고, 잔액처럼 정확도가 중요한 조회는 Primary로 라우팅해야 한다.
- 05
백업과 연결 설정도 RDS 운영의 기본 축이다. 자동 백업은 매일 지정 시간에 수행되고 최대 35일 보관되며, 수동 스냅샷은 원하는 시점에 직접 만들고 삭제 전까지 보관한다. Parameter Group은 DB 설정값을 묶어 인스턴스에 적용하는 단위이고, max_connections는 DB가 허용하는 최대 동시 연결 수다. 이 값을 넘으면 too many connections 또는 FATAL: remaining connection slots are reserved for non-replication superuser connections 같은 에러가 난다. Connection Pool은 매 요청마다 새 DB 연결을 만들고 끊는 비용을 줄이기 위해 미리 만든 연결을 재사용하는 방식이다. ECS와 Fargate에서는 전체 연결 수가 ECS Task 수 곱하기 Pool 최대값이라는 점을 먼저 계산해야 한다.
- 06
RDS Proxy는 애플리케이션과 RDS 사이에서 연결을 대신 관리하는 완전 관리형 프록시 서비스다. 핵심은 단순한 풀링이 아니라 Connection Multiplexing이다. 애플리케이션이 Proxy로 맺는 연결과 Proxy가 실제 RDS로 맺는 백엔드 연결을 분리해서, 100개의 ECS Task가 Proxy에 연결되어 있어도 실제 RDS에는 10개에서 20개 연결만 유지할 수 있다. Failover 단축 효과도 있다. Aurora MySQL은 직접 연결 평균 24초에서 RDS Proxy 경유 3.1초로 줄었고, RDS Multi-AZ MySQL은 직접 연결 평균 36.8초에서 25.1초로 줄었다. Proxy가 백엔드 연결을 유지하므로 클라이언트는 새 DNS 조회와 새 TCP, TLS 세션 수립을 건너뛸 수 있다.
- 07
RDS Proxy를 쓴다고 항상 Multiplexing 효율이 유지되는 것은 아니다. 특정 상황에서는 하나의 클라이언트 연결이 특정 백엔드 연결에 고정되는 Pinning이 발생한다. SET 명령으로 세션 변수를 설정하거나, 임시 테이블을 만들거나, GET_LOCK을 쓰거나, 멀티 스테이트먼트를 한 번에 보내거나, 트랜잭션 안에서 SET autocommit=0을 쓰면 이런 고정이 생길 수 있다. 고정된 동안에는 다른 Task가 그 백엔드 연결을 공유하지 못하므로 연결 절감 효과가 떨어진다. Pinning을 줄이려면 세션 변수 사용을 줄이고, TypeORM의 기본 설정인 준비된 구문 사용과 명시적 트랜잭션 관리를 유지하는 편이 좋다. 빈도는 DatabaseConnectionsCurrentlySessionPinned 메트릭으로 확인할 수 있다.
- 08
DB에 아예 연결되지 않는 문제는 보통 체크 순서가 정해져 있다. 먼저 RDS Security Group에서 ECS Task의 Security Group이 PostgreSQL 5432 또는 MySQL 3306 포트로 허용되어 있는지 확인한다. 다음으로 ECS Task가 RDS와 같은 VPC에 있는지, 접근 가능한 Subnet에 있는지, Private Subnet과 NAT Gateway 구성이 맞는지 본다. 그다음 DATABASE_URL의 Endpoint 주소가 정확한지 확인하고, 마지막으로 Connection Pool 크기를 본다. 로컬에서는 되는데 서버에서만 Connection timeout이나 ECONNREFUSED가 나는 경우는 이 네트워크 경계 차이에서 자주 나온다. React와 Firebase Realtime Database나 Supabase처럼 클라이언트가 직접 DB에 접근하는 경험과 달리, AWS RDS는 브라우저에서 직접 접근하는 구조가 아니다.
- 09
성능과 비용은 모니터링 지표를 기준으로 판단해야 한다. CPU 사용률이 30% 미만이고 I/O가 지속적으로 낮다면 CloudWatch의 CPUUtilization과 ReadIOPS, WriteIOPS를 2주 이상 보고 더 작은 인스턴스로 내릴 수 있다. 프로덕션처럼 상시 실행되는 DB는 1년 또는 3년 Reserved Instance로 최대 72% 절감할 수 있고, 개발이나 스테이징 DB는 AWS Instance Scheduler로 평일 09:00부터 22:00까지만 실행해 60에서 70% 절감할 수 있다. 기존 gp2 스토리지를 gp3로 바꾸면 동일 성능에서 약 20% 비용 절감이 가능하며, 트래픽이 불규칙하면 Aurora Serverless v2도 고려 대상이다.
- 10
슬로우 쿼리는 RDS 자체보다 쿼리와 인덱스 문제로 드러나는 경우가 많다. 특정 API 응답이 5초 이상 걸리고 CPUUtilization이 갑자기 높아지면, 인덱스 누락, 대량 조회, N+1 쿼리를 의심해야 한다. AWS는 기존 Performance Insights를 CloudWatch Database Insights로 통합했고, 2025년 말 기준 Performance Insights 콘솔 종료가 예정되어 있다. 실무에서는 CloudWatch Database Insights에서 Top SQL을 찾고, EXPLAIN ANALYZE로 인덱스 여부를 확인하는 흐름이 중요하다. 2025년 업데이트로는 Aurora Global Database의 Blue/Green 배포 지원과 Aurora 최대 스토리지 256TiB 확장이 있다. 정리하면 RDS 운영은 Multi-AZ와 Read Replica의 목적 구분, 연결 수 계산, Private Subnet 접근 통제, 그리고 지표 기반 확인으로 좁혀진다.
같은 레이어
L3에서 이어 듣기
- 오디오 파일
- /podcasts/l3-rds-basics.mp3