🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

RDS 읽기 전용 복제본으로 읽기 성능 향상 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 28. · 3 Views

RDS 읽기 전용 복제본으로 읽기 성능 향상 완벽 가이드

AWS RDS의 읽기 전용 복제본(Read Replica)을 활용하여 데이터베이스 읽기 성능을 향상시키는 방법을 알아봅니다. 비동기식 복제 원리부터 실제 구현, 모니터링, 그리고 장애 대응까지 실무에서 바로 적용할 수 있는 내용을 다룹니다.


목차

  1. 읽기_전용_복제본의_필요성
  2. 비동기식_복제_방식_이해
  3. 읽기_전용_복제본_생성_실습
  4. 애플리케이션에서_읽기_분산_구현
  5. 복제_지연_모니터링
  6. 읽기_복제본_승격_시나리오

1. 읽기 전용 복제본의 필요성

김개발 씨는 스타트업에서 백엔드 개발을 담당하고 있습니다. 서비스가 성장하면서 사용자가 늘어나자, 어느 날부터 데이터베이스 응답 속도가 눈에 띄게 느려지기 시작했습니다.

"왜 갑자기 이렇게 느려진 거지?" 모니터링 대시보드를 열어본 김개발 씨는 깜짝 놀랐습니다.

**읽기 전용 복제본(Read Replica)**은 원본 데이터베이스의 복사본을 만들어 읽기 요청을 분산 처리하는 기술입니다. 마치 인기 있는 맛집이 지점을 여러 개 내서 손님을 분산시키는 것과 같습니다.

원본 데이터베이스는 쓰기 작업에 집중하고, 복제본들이 읽기 요청을 나눠 처리함으로써 전체 시스템의 성능을 크게 향상시킬 수 있습니다.

다음 코드를 살펴봅시다.

// CloudWatch에서 RDS 지표 확인하기
const AWS = require('aws-sdk');
const cloudwatch = new AWS.CloudWatch({ region: 'ap-northeast-2' });

// CPU 사용률과 읽기/쓰기 비율 확인
const params = {
  MetricName: 'CPUUtilization',
  Namespace: 'AWS/RDS',
  Dimensions: [
    { Name: 'DBInstanceIdentifier', Value: 'my-database' }
  ],
  StartTime: new Date(Date.now() - 3600000), // 1시간 전
  EndTime: new Date(),
  Period: 300,
  Statistics: ['Average']
};

const metrics = await cloudwatch.getMetricStatistics(params).promise();
console.log('평균 CPU 사용률:', metrics.Datapoints);

김개발 씨는 입사 1년 차 백엔드 개발자입니다. 그가 담당하는 서비스는 론칭 초기에는 사용자가 많지 않았습니다.

데이터베이스 하나로도 충분히 모든 요청을 처리할 수 있었습니다. 그런데 서비스가 입소문을 타기 시작하면서 상황이 달라졌습니다.

사용자 수가 열 배로 늘어나자, 데이터베이스가 비명을 지르기 시작한 것입니다. 페이지 로딩 시간이 3초를 넘어가고, 심한 경우 타임아웃 에러까지 발생했습니다.

"박시니어 씨, 데이터베이스가 너무 느려졌어요. 인스턴스 사양을 올려야 할까요?" 박시니어 씨는 모니터링 대시보드를 살펴보며 고개를 저었습니다.

"일단 트래픽 패턴을 분석해 보자. 읽기와 쓰기 비율이 어떻게 되는지 확인해 봐." 분석 결과, 전체 데이터베이스 요청 중 85%가 읽기 작업이었습니다.

상품 목록 조회, 사용자 프로필 조회, 검색 결과 표시 등 대부분의 요청이 데이터를 읽어오는 작업이었던 것입니다. 반면 실제로 데이터를 변경하는 쓰기 작업은 15%에 불과했습니다.

"이런 패턴이라면 읽기 전용 복제본을 도입하는 게 정답이야." 읽기 전용 복제본이란 무엇일까요? 쉽게 비유하자면, 도서관의 대출 창구를 생각해 보세요.

인기 있는 도서관에 창구가 하나뿐이라면, 책을 빌리려는 사람과 반납하려는 사람이 한 줄로 길게 늘어설 것입니다. 이때 해결책은 간단합니다.

대출 전용 창구를 여러 개 더 만드는 것입니다. 반납 창구는 하나로 유지하되, 대출 창구를 세 개로 늘리면 대기 시간이 크게 줄어들겠죠.

데이터베이스에서도 마찬가지입니다. **원본 데이터베이스(Primary)**는 쓰기 작업을 담당하고, **읽기 전용 복제본(Read Replica)**들이 읽기 요청을 나눠서 처리합니다.

복제본은 원본의 데이터를 그대로 복사해서 가지고 있으므로, 읽기 요청에 대해 동일한 결과를 반환할 수 있습니다. AWS RDS에서는 이 기능을 아주 쉽게 사용할 수 있습니다.

클릭 몇 번으로 복제본을 생성할 수 있고, 원본 데이터베이스의 변경 사항이 자동으로 복제본에 반영됩니다. 복제본을 도입하면 얻을 수 있는 이점은 명확합니다.

첫째, 응답 속도가 빨라집니다. 읽기 요청이 여러 서버로 분산되므로 각 서버의 부하가 줄어듭니다.

둘째, 확장성이 좋아집니다. 트래픽이 더 늘어나면 복제본을 추가로 생성하면 됩니다.

셋째, 비용 효율적입니다. 무작정 원본 인스턴스의 사양을 올리는 것보다 복제본을 추가하는 것이 더 경제적인 경우가 많습니다.

김개발 씨는 박시니어 씨의 설명을 듣고 고개를 끄덕였습니다. "아, 그래서 대형 서비스들이 복제본을 사용하는 거군요!"

실전 팁

💡 - 읽기와 쓰기 비율을 먼저 분석한 후 복제본 도입을 결정하세요

  • 복제본은 최대 5개까지 생성할 수 있으며, 리전 간 복제도 가능합니다
  • CloudWatch의 ReadIOPS, WriteIOPS 지표로 읽기/쓰기 비율을 파악할 수 있습니다

2. 비동기식 복제 방식 이해

김개발 씨는 읽기 전용 복제본의 개념을 이해했지만, 한 가지 의문이 생겼습니다. "원본에 데이터가 추가되면 복제본에는 언제 반영되는 거죠?

실시간으로 동기화되나요?" 박시니어 씨가 화이트보드 앞으로 다가가며 말했습니다. "좋은 질문이야.

이게 바로 비동기식 복제의 핵심이거든."

RDS 읽기 전용 복제본은 비동기식 복제(Asynchronous Replication) 방식을 사용합니다. 원본 데이터베이스에서 변경이 발생하면, 해당 내용이 복제본에 즉시가 아닌 약간의 시차를 두고 반영됩니다.

마치 본사에서 내린 지시가 각 지점에 전달되는 데 시간이 걸리는 것과 비슷합니다. 이 방식 덕분에 원본 데이터베이스의 성능에 영향을 주지 않으면서 복제가 가능합니다.

다음 코드를 살펴봅시다.

// 비동기 복제의 동작 원리를 코드로 이해하기
// Primary DB에 데이터 쓰기
async function writeToMaster(data) {
  const masterConnection = await mysql.createConnection(masterConfig);

  // 1. Primary에 데이터 저장 (즉시 완료)
  await masterConnection.execute(
    'INSERT INTO orders (user_id, amount) VALUES (?, ?)',
    [data.userId, data.amount]
  );
  console.log('Primary DB 저장 완료');

  // 2. Binary Log에 변경 기록 (자동으로 발생)
  // 3. Replica가 Binary Log를 읽어서 적용 (비동기)
  // -> 이 과정에서 약간의 지연(Replication Lag) 발생

  return { success: true, message: '주문이 접수되었습니다' };
}

박시니어 씨가 화이트보드에 그림을 그리기 시작했습니다. 왼쪽에는 'Primary DB'라고 쓰고, 오른쪽에는 'Read Replica'라고 적었습니다.

그리고 두 박스 사이에 화살표를 그렸습니다. "데이터베이스 복제에는 크게 두 가지 방식이 있어.

동기식비동기식이지." 동기식 복제는 원본에 데이터가 저장될 때 복제본에도 동시에 저장되는 것을 보장합니다. 마치 두 사람이 같은 내용을 동시에 받아 적는 것과 같습니다.

이 방식은 데이터 일관성이 완벽하게 보장되지만, 한 가지 큰 단점이 있습니다. "동기식은 복제본이 저장을 완료할 때까지 원본도 기다려야 해.

복제본이 느려지면 원본도 덩달아 느려지는 거지." 반면 비동기식 복제는 다릅니다. 원본 데이터베이스는 자신의 저장만 완료하면 바로 클라이언트에게 응답을 보냅니다.

복제본으로의 데이터 전송은 그 이후에 별도로 진행됩니다. 이 과정을 좀 더 자세히 살펴보겠습니다.

MySQL 기반의 RDS는 Binary Log라는 것을 사용합니다. 원본 데이터베이스에서 발생하는 모든 변경 사항이 이 Binary Log에 순서대로 기록됩니다.

복제본은 이 Binary Log를 지속적으로 읽어옵니다. 새로운 내용이 추가되면 가져와서 자신의 데이터베이스에 동일하게 적용합니다.

마치 뉴스 속보를 받아보는 구독자처럼, 복제본은 원본의 변경 사항을 지속적으로 수신하고 반영하는 것입니다. "그런데 시니어님, 이렇게 하면 원본이랑 복제본 데이터가 달라질 수 있는 거 아닌가요?" 김개발 씨의 질문에 박시니어 씨가 고개를 끄덕였습니다.

"맞아. 그게 바로 Replication Lag, 복제 지연이야.

원본에 데이터가 저장된 후 복제본에 반영되기까지 시간 차이가 발생해." 보통 이 지연은 밀리초에서 수 초 정도입니다. 평상시에는 거의 느끼지 못할 정도로 짧습니다.

하지만 대량의 데이터가 한꺼번에 변경되거나, 복제본의 사양이 부족하면 지연이 길어질 수 있습니다. "그래서 읽기 복제본을 사용할 때는 Eventually Consistent, 최종적 일관성이라는 개념을 이해해야 해." 최종적 일관성이란, 지금 당장은 원본과 복제본의 데이터가 다를 수 있지만, 시간이 지나면 결국 같아진다는 의미입니다.

대부분의 읽기 작업에서는 이 정도의 일관성만으로도 충분합니다. 예를 들어 상품 목록을 조회하는 경우, 방금 추가된 신상품이 몇 초 늦게 보여도 큰 문제가 없습니다.

하지만 사용자가 방금 주문한 내역을 바로 확인하는 경우라면, 복제본이 아닌 원본에서 읽어야 정확한 정보를 보여줄 수 있습니다.

실전 팁

💡 - 비동기식 복제 덕분에 원본 DB 성능에 영향을 주지 않고 복제본을 운영할 수 있습니다

  • 복제 지연을 고려하여, 최신 데이터가 반드시 필요한 쿼리는 원본에서 읽도록 설계하세요
  • PostgreSQL RDS는 WAL(Write-Ahead Log)을 사용하고, MySQL RDS는 Binary Log를 사용합니다

3. 읽기 전용 복제본 생성 실습

이론 설명을 마친 박시니어 씨가 노트북을 열었습니다. "자, 이제 직접 만들어 보자.

백문이 불여일견이니까." 김개발 씨도 옆에 앉아 화면을 주시했습니다. AWS 콘솔에서 클릭 몇 번으로 복제본을 만들 수 있지만, 실무에서는 코드로 관리하는 것이 훨씬 안전하고 재현 가능합니다.

AWS CLI나 SDK를 사용하면 읽기 전용 복제본을 프로그래밍 방식으로 생성할 수 있습니다. createDBInstanceReadReplica API를 호출하면 원본 데이터베이스의 스냅샷을 기반으로 복제본이 만들어집니다.

복제본 생성에는 보통 5-20분 정도 소요되며, 원본 데이터베이스에는 거의 영향을 주지 않습니다.

다음 코드를 살펴봅시다.

const AWS = require('aws-sdk');
const rds = new AWS.RDS({ region: 'ap-northeast-2' });

// 읽기 전용 복제본 생성
async function createReadReplica() {
  const params = {
    // 복제본 식별자 (고유해야 함)
    DBInstanceIdentifier: 'my-database-replica-1',
    // 원본 데이터베이스 식별자
    SourceDBInstanceIdentifier: 'my-database',
    // 복제본 인스턴스 클래스 (원본과 다르게 설정 가능)
    DBInstanceClass: 'db.r5.large',
    // 가용 영역 (다른 AZ에 배치하여 가용성 향상)
    AvailabilityZone: 'ap-northeast-2b',
    // 퍼블릭 액세스 비활성화 (보안)
    PubliclyAccessible: false
  };

  const result = await rds.createDBInstanceReadReplica(params).promise();
  console.log('복제본 생성 시작:', result.DBInstance.DBInstanceIdentifier);
  return result;
}

박시니어 씨가 터미널을 열고 AWS CLI를 실행했습니다. "콘솔에서 클릭으로 만들 수도 있지만, Infrastructure as Code가 요즘 대세야.

Terraform이나 AWS CDK를 쓰는 것도 좋고, 일단은 SDK로 해볼게." 먼저 현재 운영 중인 데이터베이스의 상태를 확인했습니다. RDS 인스턴스 목록을 조회하니 'my-database'라는 이름의 Primary 인스턴스가 보였습니다.

"복제본을 만들 때 몇 가지 중요한 옵션이 있어. 하나씩 살펴보자." 첫 번째는 DBInstanceClass입니다.

복제본의 사양을 결정하는 옵션입니다. 원본과 동일한 사양을 쓸 수도 있고, 더 작거나 큰 사양을 선택할 수도 있습니다.

읽기 트래픽이 많다면 원본보다 높은 사양을 선택하는 것도 방법입니다. 두 번째는 AvailabilityZone입니다.

복제본을 어느 가용 영역에 배치할지 결정합니다. 원본과 다른 가용 영역에 배치하면, 한 곳에 장애가 발생해도 서비스가 계속 가능합니다.

이를 교차 AZ 배치라고 합니다. "저기, 복제본을 만드는 동안 원본 데이터베이스에 영향은 없나요?" 김개발 씨의 질문에 박시니어 씨가 미소 지었습니다.

"좋은 질문이야. 예전에는 스냅샷을 만드는 과정에서 원본에 부하가 걸렸어.

하지만 지금은 Copy-on-Write 방식을 쓰기 때문에 거의 영향이 없어." 복제본 생성을 실행하면 AWS가 내부적으로 여러 작업을 수행합니다. 먼저 원본의 스토리지 볼륨을 복제합니다.

그다음 새로운 EC2 인스턴스를 프로비저닝하고, 복제한 볼륨을 연결합니다. 마지막으로 Binary Log 복제를 시작하여 원본의 변경 사항을 따라잡습니다.

이 전체 과정이 완료되면 복제본의 상태가 'available'로 바뀝니다. 보통 데이터베이스 크기에 따라 5분에서 20분 정도 걸립니다.

"자, 상태를 확인해 보자." 몇 분 후, 복제본이 성공적으로 생성되었습니다. 이제 이 복제본의 엔드포인트를 애플리케이션에서 사용하면 됩니다.

원본과 복제본은 서로 다른 엔드포인트 주소를 가지므로, 애플리케이션에서 읽기와 쓰기를 구분하여 각각의 엔드포인트로 연결하면 됩니다. "한 가지 더.

복제본은 읽기 전용이야. 복제본에 INSERT나 UPDATE를 시도하면 에러가 발생해." 이것은 데이터 일관성을 보장하기 위한 설계입니다.

모든 쓰기 작업은 반드시 원본을 통해야 하고, 복제본은 오직 읽기만 담당합니다. 이 규칙 덕분에 데이터가 꼬이는 것을 방지할 수 있습니다.

실전 팁

💡 - 복제본 생성 시 원본과 다른 보안 그룹을 지정할 수 있어 네트워크 격리가 가능합니다

  • Multi-AZ 배포와 Read Replica는 다른 기능입니다. Multi-AZ는 고가용성, Read Replica는 읽기 확장용입니다
  • 복제본은 독립적인 백업 설정을 가질 수 있습니다

4. 애플리케이션에서 읽기 분산 구현

복제본이 준비되었지만, 김개발 씨에게는 또 하나의 숙제가 남았습니다. "복제본을 만들었는데, 애플리케이션에서 어떻게 사용하죠?

모든 코드를 수정해야 하나요?" 박시니어 씨가 코드 에디터를 열며 답했습니다. "현명하게 추상화하면 기존 코드는 거의 건드리지 않아도 돼."

애플리케이션에서 읽기와 쓰기를 분산하려면 커넥션 라우팅 로직이 필요합니다. 쓰기 작업(INSERT, UPDATE, DELETE)은 Primary로, 읽기 작업(SELECT)은 Replica로 보내는 것입니다.

이를 위해 데이터베이스 연결 레이어를 추상화하면, 비즈니스 로직을 수정하지 않고도 읽기 분산을 구현할 수 있습니다.

다음 코드를 살펴봅시다.

const mysql = require('mysql2/promise');

// 데이터베이스 연결 풀 구성
const dbConfig = {
  writer: {
    host: 'my-database.xxxxx.ap-northeast-2.rds.amazonaws.com',
    user: 'admin',
    password: process.env.DB_PASSWORD,
    database: 'myapp'
  },
  readers: [
    { host: 'my-database-replica-1.xxxxx.ap-northeast-2.rds.amazonaws.com' },
    { host: 'my-database-replica-2.xxxxx.ap-northeast-2.rds.amazonaws.com' }
  ]
};

class DatabaseRouter {
  constructor() {
    this.writerPool = mysql.createPool(dbConfig.writer);
    this.readerPools = dbConfig.readers.map(reader =>
      mysql.createPool({ ...dbConfig.writer, ...reader })
    );
    this.currentReaderIndex = 0;
  }

  // 쓰기용 커넥션 반환
  getWriter() { return this.writerPool; }

  // 읽기용 커넥션 반환 (라운드 로빈)
  getReader() {
    const pool = this.readerPools[this.currentReaderIndex];
    this.currentReaderIndex = (this.currentReaderIndex + 1) % this.readerPools.length;
    return pool;
  }
}

김개발 씨는 코드를 살펴보며 질문했습니다. "그러니까 SELECT 쿼리는 getReader()로, INSERT나 UPDATE는 getWriter()로 보내면 되는 거죠?" 박시니어 씨가 고개를 끄덕였습니다.

"맞아. 하지만 매번 개발자가 이걸 신경 쓰면 실수하기 쉬워.

그래서 한 단계 더 추상화하는 게 좋아." 실무에서는 Repository 패턴이나 ORM을 활용하여 이 로직을 숨기는 경우가 많습니다. 개발자는 그냥 findById()나 save() 같은 메서드만 호출하고, 내부적으로 적절한 커넥션이 선택되도록 하는 것입니다.

"그런데 한 가지 주의할 점이 있어. 트랜잭션 안에서는 모든 쿼리가 같은 커넥션을 써야 해." 만약 트랜잭션 안에서 읽기와 쓰기가 섞여 있다면, 모두 Writer를 사용해야 합니다.

중간에 Replica로 읽기를 보내면 트랜잭션 격리가 깨질 수 있습니다. 또 하나 중요한 상황이 있습니다.

바로 쓰기 직후 읽기입니다. 사용자가 게시글을 작성하고 바로 확인 페이지로 이동하는 경우를 생각해 보세요.

게시글이 Writer에 저장된 직후, Replica에서 읽으면 아직 복제가 완료되지 않아 빈 결과가 나올 수 있습니다. "이런 경우를 Read-Your-Writes 문제라고 해.

해결 방법은 몇 가지가 있어." 첫 번째 방법은 쓰기 직후 일정 시간 동안은 Writer에서 읽는 것입니다. 세션에 마지막 쓰기 시간을 기록해 두고, 그로부터 1-2초 이내의 읽기는 Writer로 보내는 방식입니다.

두 번째 방법은 쿼리 힌트를 사용하는 것입니다. 특정 쿼리는 반드시 최신 데이터가 필요하다고 명시하면, 해당 쿼리만 Writer에서 실행합니다.

세 번째 방법은 애플리케이션 레벨에서 복제 지연을 체크하는 것입니다. Replica의 지연이 허용 범위를 넘으면 자동으로 Writer로 폴백하는 로직을 구현할 수 있습니다.

김개발 씨는 고개를 끄덕이며 메모했습니다. "생각보다 고려할 게 많네요.

그냥 복제본 만들면 끝인 줄 알았는데." 박시니어 씨가 웃으며 대답했습니다. "분산 시스템이 다 그래.

하지만 이 패턴만 잘 익혀두면 어떤 규모의 서비스도 감당할 수 있어."

실전 팁

💡 - AWS RDS Proxy를 사용하면 커넥션 관리와 읽기/쓰기 분리를 자동화할 수 있습니다

  • 트랜잭션 내의 모든 쿼리는 반드시 동일한 Writer 커넥션을 사용하세요
  • ORM을 사용한다면 read/write 커넥션 분리를 지원하는지 확인하세요 (Sequelize, TypeORM 등 지원)

5. 복제 지연 모니터링

읽기 분산 시스템이 운영에 들어간 지 일주일이 지났습니다. 어느 날 오후, 고객센터에서 연락이 왔습니다.

"주문했는데 주문 내역에 안 보여요." 김개발 씨는 식은땀을 흘리며 모니터링 대시보드를 열었습니다. 복제 지연이 30초를 넘어가고 있었습니다.

**Replication Lag(복제 지연)**은 읽기 전용 복제본 운영에서 가장 중요한 모니터링 지표입니다. 원본에 저장된 데이터가 복제본에 반영되기까지 걸리는 시간을 나타냅니다.

평소에는 밀리초 수준이지만, 대량 배치 작업이나 복제본 리소스 부족 시 급격히 증가할 수 있습니다. CloudWatch에서 ReplicaLag 지표를 모니터링하고 알람을 설정해야 합니다.

다음 코드를 살펴봅시다.

const AWS = require('aws-sdk');
const cloudwatch = new AWS.CloudWatch({ region: 'ap-northeast-2' });

// 복제 지연 모니터링 및 알람 설정
async function setupReplicationLagAlarm() {
  const params = {
    AlarmName: 'RDS-ReplicaLag-High',
    MetricName: 'ReplicaLag',
    Namespace: 'AWS/RDS',
    Dimensions: [
      { Name: 'DBInstanceIdentifier', Value: 'my-database-replica-1' }
    ],
    Statistic: 'Average',
    Period: 60,  // 1분 단위
    EvaluationPeriods: 3,  // 3번 연속 초과시 알람
    Threshold: 5,  // 5초 초과시
    ComparisonOperator: 'GreaterThanThreshold',
    AlarmActions: ['arn:aws:sns:ap-northeast-2:123456789:alerts']
  };

  await cloudwatch.putMetricAlarm(params).promise();
  console.log('복제 지연 알람 설정 완료');
}

// 현재 복제 지연 확인
async function checkReplicaLag(replicaId) {
  const lag = await cloudwatch.getMetricStatistics({
    Namespace: 'AWS/RDS',
    MetricName: 'ReplicaLag',
    Dimensions: [{ Name: 'DBInstanceIdentifier', Value: replicaId }],
    StartTime: new Date(Date.now() - 300000),
    EndTime: new Date(),
    Period: 60,
    Statistics: ['Average']
  }).promise();

  return lag.Datapoints;
}

김개발 씨의 손이 떨리고 있었습니다. 고객이 주문을 완료했는데 주문 내역이 보이지 않는다니, 이건 심각한 문제입니다.

"침착해. 일단 지표부터 확인하자." 박시니어 씨가 CloudWatch 대시보드를 열었습니다.

ReplicaLag 지표를 보니 30초를 넘기고 있었습니다. 평소 0.1초 이하였던 것과 비교하면 엄청난 수치입니다.

"원인이 뭘까요?" 함께 다른 지표들을 살펴보았습니다. CPU 사용률, 메모리, 디스크 I/O...

모든 것이 정상 범위였습니다. 그런데 WriteIOPS 그래프가 눈에 들어왔습니다.

평소보다 열 배나 높은 수치를 보이고 있었습니다. "아, 이거다.

오늘 오후에 데이터 마이그레이션 배치 작업이 있었잖아." 대량의 데이터가 한꺼번에 Primary에 입력되면서, Binary Log에 엄청난 양의 변경 기록이 쌓인 것입니다. Replica가 이를 따라잡는 데 시간이 걸리면서 지연이 발생했습니다.

"당장 할 수 있는 건 배치 작업을 일시 중지하는 거야. 그리고 나서 Replica가 따라잡을 시간을 줘야 해." 배치 작업을 중지하자, ReplicaLag가 서서히 줄어들기 시작했습니다.

5분 후 정상 수준으로 돌아왔습니다. "이런 일이 다시 발생하지 않으려면 어떻게 해야 할까요?" 박시니어 씨가 몇 가지 방안을 제시했습니다.

첫째, 배치 작업 시간 분산입니다. 한 번에 대량으로 처리하지 말고, 여러 번에 나눠서 처리합니다.

각 배치 사이에 슬립 타임을 두면 Replica가 따라잡을 여유가 생깁니다. 둘째, 알람 설정입니다.

ReplicaLag가 일정 수준을 넘으면 즉시 알림을 받을 수 있도록 CloudWatch 알람을 설정합니다. 5초 이상이면 경고, 30초 이상이면 심각으로 분류하는 것이 일반적입니다.

셋째, 자동 대응 로직입니다. 애플리케이션에서 ReplicaLag를 주기적으로 체크하고, 임계값을 초과하면 자동으로 모든 읽기를 Primary로 보내는 폴백 로직을 구현합니다.

김개발 씨는 바로 알람 설정 작업에 들어갔습니다. 3분간 평균 ReplicaLag가 5초를 초과하면 Slack으로 알림이 오도록 구성했습니다.

"앞으로는 대규모 배치 작업 전에 미리 공유하고, 트래픽이 적은 시간대에 진행하도록 합시다." 이날의 교훈은 명확했습니다. 읽기 전용 복제본은 만능이 아닙니다.

꾸준한 모니터링과 적절한 운영 전략이 함께해야 안정적인 시스템을 유지할 수 있습니다.

실전 팁

💡 - ReplicaLag 알람은 5초, 30초 두 단계로 설정하는 것을 권장합니다

  • 대량 배치 작업은 트래픽이 적은 시간대에 분산하여 실행하세요
  • Enhanced Monitoring을 활성화하면 1초 단위의 상세한 지표를 확인할 수 있습니다

6. 읽기 복제본 승격 시나리오

새벽 3시, 김개발 씨의 핸드폰이 울렸습니다. "Primary DB 인스턴스 장애 발생!" 잠에서 깬 김개발 씨의 심장이 빠르게 뛰기 시작했습니다.

서비스가 완전히 멈춘 상황. 하지만 박시니어 씨가 미리 알려준 복제본 승격 절차가 머릿속에 떠올랐습니다.

읽기 전용 복제본은 필요시 **독립적인 Primary 인스턴스로 승격(Promote)**될 수 있습니다. 원본 데이터베이스에 장애가 발생했을 때, 복제본을 승격시켜 서비스를 빠르게 복구할 수 있습니다.

승격된 인스턴스는 더 이상 복제본이 아닌 독자적인 데이터베이스가 되어 읽기와 쓰기가 모두 가능해집니다.

다음 코드를 살펴봅시다.

const AWS = require('aws-sdk');
const rds = new AWS.RDS({ region: 'ap-northeast-2' });

// 읽기 복제본을 Primary로 승격
async function promoteReadReplica(replicaId) {
  console.log(`복제본 ${replicaId} 승격 시작...`);

  // 승격 명령 실행
  const result = await rds.promoteReadReplica({
    DBInstanceIdentifier: replicaId,
    BackupRetentionPeriod: 7,  // 승격 후 백업 보존 기간
    PreferredBackupWindow: '03:00-04:00'  // 백업 시간대
  }).promise();

  console.log('승격 시작됨. 상태:', result.DBInstance.DBInstanceStatus);

  // 승격 완료 대기
  await rds.waitFor('dBInstanceAvailable', {
    DBInstanceIdentifier: replicaId
  }).promise();

  console.log('승격 완료! 새로운 엔드포인트:',
    result.DBInstance.Endpoint.Address);

  return result.DBInstance.Endpoint;
}

// 애플리케이션 연결 정보 업데이트 (예: Parameter Store)
async function updateConnectionEndpoint(newEndpoint) {
  const ssm = new AWS.SSM({ region: 'ap-northeast-2' });
  await ssm.putParameter({
    Name: '/myapp/database/writer-endpoint',
    Value: newEndpoint,
    Type: 'String',
    Overwrite: true
  }).promise();
}

새벽 3시의 장애 알림. 모든 개발자에게 악몽 같은 상황입니다.

하지만 김개발 씨는 침착하게 노트북을 열었습니다. 박시니어 씨가 만들어둔 장애 대응 매뉴얼이 있었기 때문입니다.

"첫 번째 단계: 장애 상황 파악." CloudWatch를 열어보니 Primary DB의 상태가 'failed'로 표시되어 있었습니다. 원인을 파악하는 것보다 서비스 복구가 우선입니다.

"두 번째 단계: 복제본 상태 확인." 다행히 읽기 복제본 두 대는 모두 정상이었습니다. 가장 최근까지 동기화된 복제본을 선택해야 합니다.

ReplicaLag를 확인하니 둘 다 0.5초 이하였습니다. replica-1을 승격 대상으로 선택했습니다.

복제본 승격이란 무엇일까요? 쉽게 말해, 부사령관을 사령관으로 진급시키는 것과 같습니다.

읽기 전용이었던 복제본이 완전한 권한을 가진 독립 데이터베이스가 됩니다. 승격 과정에서 몇 가지 중요한 일이 일어납니다.

첫째, 복제가 중단됩니다. 더 이상 원본으로부터 데이터를 받지 않습니다.

둘째, 읽기 전용 제한이 해제됩니다. 이제 INSERT, UPDATE, DELETE가 가능해집니다.

셋째, 자동 백업이 활성화됩니다. 독립 데이터베이스로서 자체 백업을 시작합니다.

"세 번째 단계: 승격 실행." 김개발 씨는 준비해둔 스크립트를 실행했습니다. promoteReadReplica API가 호출되고, 복제본의 상태가 'modifying'으로 바뀌었습니다.

승격에는 보통 1-2분이 소요됩니다. 이 시간 동안 해당 인스턴스는 재시작됩니다.

짧은 시간이지만, 서비스가 완전히 중단된 상황에서는 길게 느껴집니다. "네 번째 단계: 애플리케이션 연결 변경." 승격이 완료되면 애플리케이션의 데이터베이스 연결 정보를 변경해야 합니다.

기존 Primary의 엔드포인트 대신 승격된 인스턴스의 엔드포인트를 사용해야 합니다. 김개발 씨의 팀에서는 AWS Parameter Store에 데이터베이스 엔드포인트를 저장해 두고 있었습니다.

이 값을 업데이트하고 애플리케이션을 재시작하면 됩니다. 5분 후, 서비스가 정상화되었습니다.

새벽 장애였지만, 사전에 준비된 절차 덕분에 빠르게 복구할 수 있었습니다. "아, 한 가지 중요한 게 있어." 나중에 박시니어 씨가 덧붙였습니다.

"승격된 복제본은 더 이상 원본과 연결되어 있지 않아. 나중에 원래 Primary가 복구되더라도, 두 데이터베이스는 별개야.

데이터 동기화 작업이 필요해." 장애 복구 후에는 시스템 구성을 다시 정상화해야 합니다. 새로운 복제본을 생성하거나, 원래의 Primary를 복구하여 복제본으로 전환하는 등의 후속 작업이 필요합니다.

실전 팁

💡 - 승격 전 ReplicaLag를 확인하여 가장 최신 데이터를 가진 복제본을 선택하세요

  • Route 53 DNS를 사용하면 엔드포인트 변경 없이 DNS 레코드만 업데이트할 수 있습니다
  • 정기적으로 승격 훈련을 실시하여 실제 장애 시 빠르게 대응할 수 있도록 하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#AWS#RDS#ReadReplica#Database#Performance#AWS,RDS,Database,Performance

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.