✅ 왜 읽기 전용 분산 구조가 필요한가? Replica - "읽기 성능"을 위한 분산 구조
서비스 트래픽이 늘어나면 DB에 큰 부하가 발생합니다. 그런데, 예시로 쇼핑몰 서비스에서 트래픽의 90% 이상은 상품 조회, 카테고리, 리뷰 보기 와 같은 SELECT 요청이고, 그 나머지가 장바구니 담기, 결제, 후기 작성 등의 INSERT / UPDATE 요청입니다. 트래픽이 증가하게 되면 한 개의 DB서버에서는 모든 SELECT가 Primary DB로 몰리게 되고, 결제, 재고 감소 등의 요청은 밀려서 지연이 되게 됩니다. 그러면 DB CPU 100%, 슬로우 쿼리 증가, 전체 서비스가 느려지고 "상품은 잘 보이는데 결제가 안 돼요" 같은 문제 발생하게 됩니다.
이처럼 **SELECT 요청(읽기)**이 많을 경우, 이를 분산시켜야 성능과 안정성을 확보할 수 있습니다. Read Replica는 SELECT(읽기) 요청을 다른 서버로 분산하기 위한 목적에서 등장한 구조입니다.
Primary-Replica(Write-Read) DB Replication 구조에서 Replica는 SELECT만 받고, Primary는 쓰기를 담당해서 여유있는 트랜잭션 처리가 가능하고 전체 서비스 응답속도도 향상되게 됩니다.
🔧 실무에서 쓰이는 상황 예시
상황 | 이유 |
앱에서 게시글, 상품 목록, 댓글 등 대량 조회 | Replica에 분산 |
관리자 리포트/통계 시스템 | 오래 걸리는 SELECT는 Replica 전용 |
Primary 과부하 이슈 | SELECT 트래픽만 따로 분산 |
결제/회원 정보는 최신 데이터 필요 | 이때는 Primary만 사용 (정합성 중요) |
📌 참고로, 레플리카와 서버이중화는 전혀 다른 개념입니다. 레플리카는 성능 향상 (읽기 부하 분산), 서버 이중화 (HA)는 장애 대비 (서버가 죽어도 자동 전환) 를 목적으로 합니다.
레플리카는 1인 주방장 주문 다받는 음식점에서 홀만 담당하는 직원을 하나 더 채용해서 속도를 빠르게 한 것이라면
서버이중화는 주방장을 복제해서 한 명이 일을 못할 때 다른 사람이 대체할 수있도록 끊임없는 서비스 가능하게 하는 것입니다. 전혀 다른 목적이고 실제 동작도 다릅니다.
"레플리카가 Primary 죽으면 자동 전환돼요?" → ❌ 안 됨
"이중화 구성이면 SELECT도 나눠서 쓰나요?" → ❌ 주 목적이 다름
🏗️ Primary / Replica 구조란?
📌 구조 개요
역할 | 설명 |
Primary (Master) | 모든 쓰기(INSERT/UPDATE/DELETE) 작업 담당 |
Replica (Slave) | 읽기(SELECT) 전용 DB, 실시간으로 Primary의 데이터를 복제받음 (약간의 지연 존재) |
데이터 복제 | Primary의 binlog → Replica가 비동기/반동기 방식으로 받아서 반영 |
📊 아키텍처 예시
애플리케이션
├── 쓰기 요청 → Primary DB
└── 읽기 요청 → Replica DB1, DB2 ...
Read Replica는 "DB의 읽기 트래픽을 분산시켜서, 성능 향상과 장애 예방을 동시에 노리는 구조"입니다.
✅ 읽기 부하 분산 전략
📌 방법 1: DB Driver 수준에서 라우팅
- 예: MySQL Connector/J 8.x 이상 → replicationDriver 지원
jdbc:mysql:replication://primary-db,replica-db1,replica-db2/mydb
- 자동으로 쓰기는 Primary로, 읽기는 Replica로 분산
📌 방법 2: 애플리케이션 코드에서 라우팅 (Spring 기준)
- AbstractRoutingDataSource 활용하여 쿼리 유형에 따라 DataSource 분기
public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {
protected Object determineCurrentLookupKey() {
return TransactionSynchronizationManager.isCurrentTransactionReadOnly()
? "read"
: "write";
}
}
- @Transactional(readOnly = true) → Replica
- 일반 트랜잭션 → Primary
🧨 실시간 복제의 이슈들
⚠️ 1. Replication Lag (지연)
- 복제 지연으로 인해 쓰기 직후 읽으면 최신 데이터가 안 보일 수 있음
- 예: 사용자가 회원가입을 했는데, 직후 프로필을 조회하니 없다고 뜨는 상황
해결 전략:
- 쓰기 직후 특정 요청은 강제로 Primary 사용
- 세션/캐시 기반의 Read-After-Write 처리
⚠️ 2. 일관성 보장 문제
- 트랜잭션 내에서 SELECT → Replica 사용 시,
- 동시에 다른 트랜잭션이 INSERT한 값이 보이지 않을 수 있음
해결 전략:
- 트랜잭션 내에서는 무조건 Primary 사용
- @Transactional(readOnly = true) → 트랜잭션 외부에서만 적용 권장
🔍 read-replica 사용할 때의 주의점
항목 | 설명 |
❗ 트랜잭션 내 SELECT | Primary만 사용해야 정합성 유지 |
❗ JOIN, Aggregation | Replica 성능 낮으면 병목 발생 가능 |
❗ 읽기 결과 캐싱 | 지연된 데이터 반영 시 주의 필요 |
✅ 백오피스 / 보고서 / 통계 | Replica 사용 적합한 케이스 |
🧠 실무 운영 팁
- Replica는 반드시 모니터링해야 함 (지연 초 단위, replication 상태)
- MySQL: SHOW SLAVE STATUS\\G / SHOW REPLICA STATUS
- Prometheus + Grafana → Seconds_Behind_Master 지표 모니터링
- Primary 장애 발생 대비 → 자동 Failover 구성 고려 (MHA, Orchestrator 등)
✅ 마무리 정리
구성 요소 | 역할 |
Primary | 쓰기 전용, 단일 노드 중심 |
Replica | 읽기 전용, 다수 구성 가능 |
라우팅 방식 | JDBC Driver or Application 코드 |
주의할 점 | 지연, 정합성, 트랜잭션 내 SELECT 처리 |
👉 다음 글에서는 WAS 레벨에서의 사용자 세션 처리 방식을 정리합니다.
'database' 카테고리의 다른 글
🔐 트랜잭션 격리 수준과 세션 관계 – 성능과 일관성의 줄다리기 | READ COMMITTED, SERIALIZABLE | 트랜잭션과 세션/커넥션의 생명 주기 관계 (0) | 2025.04.06 |
---|---|
🩺 오라클 기준 DB 세션 모니터링 방법( v$session, v$process, v$sql ) | 세션정보 확인 추적 -> 문제세션 찾는 방법 -> 자동 모니터링 알람 설정 (0) | 2025.04.06 |
🔧 커넥션 풀 튜닝 전략 – 성능 병목을 잡는 핵심 포인트 (0) | 2025.04.06 |
데이터베이스_index (0) | 2025.04.06 |
[DB] HikariCP 커넥션 풀 왜쓰는거야? | 작동 흐름 | 커넥션 풀 없는 구조 vs 있는 구조 비교 | 세션모니터링 (0) | 2025.04.06 |
댓글