リードレプリカ

プライマリ DB の読み取り専用コピーで、読み取り負荷を分散しスケーラビリティを向上させる

データベーススケーリング

リードレプリカとは

リードレプリカは、プライマリ DB の読み取り専用コピーで、読み取り負荷を分散しスケーラビリティを向上させる。書き込みはプライマリのみ、読み取りはレプリカに分散する。

可用性のための待機系とは別物である。RDS の Multi-AZ 構成 (別の AZ にスタンバイを 1 台置く形) のスタンバイは読み取りに使えず、読み取り容量を増やしたければリードレプリカを別に足す。読み取りも受けられるのは Multi-AZ DB クラスター構成のリーダーで、こちらは高可用性と読み取り分散を兼ねる。

仕組み

プライマリ DB への書き込みが非同期でリードレプリカにレプリケーションされる。読み取りリクエストをリードレプリカに分散することで、プライマリの負荷を軽減し、読み取りスループットを向上させる。ただし非同期レプリケーションのため、書き込み直後にリードレプリカを読むと古いデータが返る可能性がある (レプリケーション遅延)。

書き込み → プライマリ DB
              ↓ 非同期レプリケーション
読み取り → リードレプリカ 1
読み取り → リードレプリカ 2
読み取り → リードレプリカ 3

遅延がどこで生まれるかは方式で違う。RDS のリードレプリカはプライマリとは別のインスタンスで、プライマリのトランザクションログを受け取って同じ更新を自分のストレージに再生する。この再生は多くのエンジンで直列に進むため、プライマリの書き込みが増えるほど適用が追いつかず遅延が伸びる。長いトランザクションや一括更新も同じ理由で遅延の山を作る。遅延は CloudWatchReplicaLag (Aurora では AuroraReplicaLag) で観測できるので、閾値を決めて監視しておく。

Aurora は同一リージョン内ではレプリカがプライマリと同じクラスタボリュームを共有する。データを送って再生する工程が無いぶん遅延が小さく、AWS は数十ミリ秒程度を典型値として案内している。別リージョンへ延ばす場合、Aurora Global Database の物理レプリケーションでは通常 1 秒未満、バイナリログによる論理レプリケーションでは変更量とネットワークの遅れしだいで伸び得る。

Aurora のリードレプリカ

RDS と Aurora では台数の上限と遅延の桁が違うため、読み取りをどこまでレプリカへ寄せられるかも変わる。

観点RDS リードレプリカAurora リードレプリカ
最大数15 (MySQL / MariaDB / PostgreSQL / SQL Server。Oracle は 5)15
レプリケーション遅延書き込み量と再生の進みしだい同一リージョンで数十ミリ秒程度
フェイルオーバー手動昇格自動フェイルオーバー
ストレージ独立共有 (クラスタボリューム)

台数を足しても書き込みが速くならない点は共通である。書き込みが詰まっているならレプリカではなく、インスタンスの拡大かデータの分割で対処する。読み取りが詰まっているなら 1 台足して、遅延の指標と接続先の偏りを合わせて見る。

DynamoDB のリードレプリカ

DynamoDB はリージョン内で自動的に 3 つの AZ へデータを複製する。台数や配置を指定する余地は無く、こちらで選べるのは読み取りごとの整合性だけである。

// 結果整合性 (デフォルト): どの複製から返るかは決まっていない (速く、安い)
await db.get({ TableName: 'users', Key: { id: '123' } });

// 強い整合性: 直前の書き込みまで反映された値が返る (遅く、高い)
await db.get({ TableName: 'users', Key: { id: '123' }, ConsistentRead: true });

強い整合性の読み取りには制約が 3 つある。4 KB までの項目で消費するのは 1 読み取り単位で、結果整合性の 0.5 単位に対して 2 倍かかる。グローバルセカンダリインデックスに対しては指定できない。レイテンシも結果整合性より上がる。既定を強い整合性へ寄せると費用と応答時間の両方に効くため、必要な読み取りにだけ付ける。

リードレプリカの注意点

つまずくのは遅延そのものより、遅延がある前提で書かれていないコードである。

注意点対策
書き込み直後の読み取りで古い値が返るその読み取りだけプライマリへ向ける (次節の Read Your Own Writes)
遅延が伸びても気づけない遅延の指標を監視し、閾値を超えたら読み取りをプライマリへ寄せる
昇格は自動では起きないRDS のリードレプリカ昇格は手動操作。切り替え手順と接続先の変更方法を先に用意する
台数を増やしても書き込みは速くならない書き込みが上限ならインスタンスの拡大かデータの分割で対処する

Read Your Own Writes

自分が書いた値だけは必ず自分に見えるようにする、という線引きである。すべての読み取りを強い整合性にすると費用と応答時間を捨てることになるので、書き込み直後の 1 回だけを強い整合性にする。

// 書き込み直後はプライマリから読み取り
await db.put({ TableName: 'users', Item: updatedUser });

// 直後の読み取り: 強い整合性
const user = await db.get({
  TableName: 'users',
  Key: { id: updatedUser.id },
  ConsistentRead: true,
});
// それ以外の読み取り: 結果整合性 (デフォルト)

SQL 系でも考え方は同じで、書き込みを行ったセッションだけを一定時間プライマリへ固定するか、更新した行の識別子を持ち回って該当の読み取りだけプライマリへ回す。利用者ごとに「直前に書いたか」を持てば、影響を受けるのはその利用者の数回の読み取りだけで済む。

リードレプリカ vs キャッシュ

どちらも読み取りを軽くする道具だが、効く相手が違う。

観点リードレプリカキャッシュ (DAX, ElastiCache)
データの鮮度数ミリ秒〜数秒遅れTTL に依存
一貫性結果整合性キャッシュミス時に DB から取得
コストDB インスタンス費用キャッシュノード費用
用途読み取り負荷の分散低レイテンシの読み取り

選び分けの目安は、読み取りが同じキーに集中しているかどうかである。集中しているならキャッシュが効き、多様なクエリが広く来るならレプリカのほうが素直に効く。DynamoDB の DAX には注意点があり、結果整合性の読み取りはキャッシュから返るが、強い整合性を指定した読み取りは DAX を素通りして DynamoDB へ行き、結果もキャッシュされない。強い整合性が既定になっている経路をそのまま DAX に載せても、キャッシュヒットは増えない。

この記事は役に立ちましたか?

関連用語

関連する記事