🌌

AWS Auroraを論文から読み解いてみる

に公開

はじめに

現在インターンとして参画してるプロジェクトで本番DBにAWS Auroraを使っています。
結構前にリリースされたクラウドサービスなので今更ですが、AWS Auroraのアーキテクチャや長所などについて論文を読みながら解説、理解していきたいと思います。
なお、論文読解シリーズは今後SpannerやDynamoを始めとした分散システムについてやっていこうかなと思ってます。

この記事の目的

Auroraのアーキテクチャを1次ソースを元に理解し、なぜ導入すべきか?トレードオフは何か?などの設計判断に役に立てることを想定しています。

Auroraとは?簡単に

AWS Auroraは、「ログこそがデータベースである」という思想に基づき、レコードの書き込み時にREDO Log(テーブルの変更履歴)のみを永続化し、実際のデータページへのコミットはストレージノードが非同期で行う(計算機能とストレージ機能の分離)ことで高い可用性とスケーラビリティを実現したデータベースです。

いつ使うべき?

後半でも改めて解説しますが、Auroraは、

  • 軽い更新処理が高頻度で発生するOLTP
  • 更新後即時に参照することが求められるアプリケーション
  • 大規模な集計クエリが必要になるダッシボードなど
    で利用するとメリットが得られるでしょう。

逆に、以下のようなケースではあまり恩恵が受けられないかもしれません。

  • IO負荷が低い小規模なワークロード
  • コスト最小化を最優先し、高い耐久性や可用性を必要としない場合

背景

ここからは論文の内容に踏み込みながら解説していきます。
まず、Auroraが前提としている背景として、クラウドの普及に伴ってデータベースのボトルネックがストレージや計算能力からネットワークIOへと移行したことが挙げられています。具体的には、データベース階層とストレージ階層の間の通信やレプリケーションに伴うトラフィックの増幅がパフォーマンスを左右します。
従来のデータベースは、キャッシュミス時のディスクアクセスやトランザクションコミット時の 2PC (Two-Phase Commit, 2相コミット) のような同期的プロトコルによって処理の停滞(ストール)やコンテキストスイッチが生じ、高いレイテンシや耐障害性の問題などを抱えていました。

そこでAuroraは、「計算とストレージの分離」をより積極的に推し進めた新しいアーキテクチャを採用し、これらの問題を解決しようとしました。
本論文では大別して4つの新規性が挙げられています。

  1. 独立して耐障害性が高く自己修復が可能なシステムを、複数のデータセンターをまたいで作ることで、パフォーマンスのばらつきや障害からデータベースを守ることができる。

  2. ログこそがデータベース」 データベースエンジンからredoログの処理のみを、専用のマルチテナント型分散ストレージサービスにプッシュダウン(オフロード)します。

  3. 非同期的な分散合意 従来のコストが高く「お喋りな(chatty)」合意プロトコルを避け、効率的な非同期スキームによって分散ストレージ全体で耐久性のある状態を維持します。

前提: Auroraが差し替えたのは既存のDBのどこか?

各新規性の話に入る前に、Aurora と MySQL/InnoDB の関係を押さえておきましょう。ここを先に掴んでおくと、以降の書き込みや読み取りフローが「MySQL と共通の部分」「Aurora 固有の部分」の2層に分かれて見えるようになります。

InnoDB の書き込みパスを上から下まで並べると、ざっくり以下の階層です。

(1) SQL 層(parse / optimize)

(2) ハンドラ層(InnoDBへの入口)

(3) B+Tree 層(インデックス探索・ページ分割など)

(4) ページ操作層(MTR = mini-transaction で包まれる)

(5) バッファプール / redoログ生成

(6) ストレージ I/O(ディスク書き込み)

Aurora が差し替えたのは (6) だけです。(1)〜(5) はMySQLのInnoDBがほぼそのまま動いています。

  • 「どのページにアクセスするか」を決めるのは (3) B+Tree 層(主キーB+Treeを辿り、InnoDBのページ番号 = ボリューム内オフセットでページを特定する)
  • 「そのページをどう変更し、どんな redo レコードを出すか」は (4)(5) の仕事(physiological な redo を MTR で束ねて吐く)
  • 従来はその redo とデータページをローカルディスクに書いていた (6) を、Aurora は「redo を6つの分散ストレージノードにネットワーク送信する」だけに置き換えた

言い換えると:

Aurora の賢さは「B+Tree とログ生成はそのまま、その下の I/O 経路だけ分散ストレージに差し替えた」点にある。

この分離のおかげで、

  • 既存の InnoDB ロジック(インデックス、MVCC、ロック等)がそのまま動く → MySQL互換
  • redo 形式も InnoDB 互換 → 既存の知見が使える
  • 同時に、ストレージ層の耐久性・I/O効率だけを新アーキテクチャで刷新できた

以降の書き込み/読み取りフローでは、「どのページにアクセスするか」のところは InnoDB の B+Tree 探索と思って読んでもらい、「ページを読み書きする実体」のところから Aurora 固有の話に入っていきます。

新規性1: クラウドスケールにおける耐久性の再定義とクォーラム設計

本章では、クラウド環境で起こる特有の故障ケースを基に、Auroraがどのように障害に対してロバストな設計を実現させているかを解説していきます。

前提: AZ(Availability Zone)と相関故障

AZとは、

  • 他のAZと低遅延のリンクで接続されている領域の単位
  • 電源・ネットワーク・物理障害・ソフトウェアデプロイなどの単位が分離されている

ものを指します。AZ同士はおおむね独立した故障ドメインとみなせるため、複数AZにレプリカを分散させれば「1つのAZが丸ごと落ちる」ような大規模障害にも耐えられそうに見えます。しかし論文は、ここで起きる相関故障 (Correlated Failures) に注意を促しています。AZ全体の停止は、その内部にある全ディスク・全ノードが「同時に」故障することと等価であり、単純な独立故障の積算では確率を見誤ります。

Auroraのクォーラム

Auroraは投票型クォーラムに基づいて耐久性を議論しています。複製数を V, 書き込みクォーラムを V_w, 読み取りクォーラムを V_r としたとき、整合性のためには次の2条件が必要です。

  • V_r + V_w > V (読み取りが必ず最新の書き込みと交差する)
  • V_w > V/2 (書き込み同士の衝突を防ぐ)

単一ノード障害だけを想定するなら V=3, V_w = V_r = 2 の「2/3クォーラム」で十分です。しかし論文は明確に次のように述べています。

"We believe 2/3 quorums are inadequate."

理由は前述の相関故障です。3つのAZに1コピーずつ置く2/3構成では、AZが1つ丸ごと落ちた瞬間に残り2コピーのうち1つでも個別障害(ディスクやノードの故障)が起きると、整合性条件を満たせなくなります。大規模なフリートでは常に "background noise of failures" が存在するため、この同時発生は無視できない確率で起こります。

そこでAuroraは、設計ゴールとして AZ+1 を掲げます。

"(a) losing an entire AZ and one additional node (AZ+1) without losing data, and (b) losing an entire AZ without impacting the ability to write data."

これを満たす構成が 3つのAZ × 各2コピー = 計6コピー、V_w = 4, V_r = 3 です。

  • AZ全損 + 追加1ノード(計3ノード喪失)でも、残り3コピーで V_r = 3 を満たし読み取り可能
  • 任意の2ノード障害(AZ丸ごと含む)でも、残り4コピーで V_w = 4 を満たし書き込み可能
  • 読み取りクォーラムさえ生きていれば、追加レプリカを生成して書き込みクォーラムを再構築できる

耐障害性を担保するには?

まず以下の用語を押さえておきましょう。

  • MTTF (Mean Time To Failure): 1つのコンポーネントが壊れるまでの平均時間。「どれくらい壊れにくいか」を表す。値が大きいほど故障しにくい。
  • MTTR (Mean Time To Repair): 壊れたコンポーネントを復旧し終えるまでの平均時間。「どれくらい早く戻せるか」を表す。値が小さいほど復旧が速い。

上記のクォーラムを設計しただけでは話は終わりません。AZ+1構成も、修復が完了する前にさらに障害が重なれば破綻します。問題は 二重障害が発生する確率 であり、これは

P(\text{double failure}) \approx \text{failure rate} \times \text{MTTR}

で近似できます。論文の立場は明快で、

"It is difficult, past a point, to reduce the probability of MTTF on independent failures. We instead focus on reducing MTTR to shrink the window of vulnerability to a double fault."

つまり、独立故障のMTTFを下げるのは限界があるので、MTTR(修復時間)を短くして「脆弱な窓」を縮める方向に振り切る、というアプローチです。

MTTRを縮めるための具体的な仕掛けが Segmented Storage です。

  • データベースボリュームを 固定長10GBのセグメント に分割
  • 各セグメントを6つに複製したものを Protection Group (PG) と呼び、これが障害・修復の最小単位になる
  • ボリュームはPGの連結(最大64TB)

ここでポイントになるのは、「単位が小さい」ことそのものよりも、修復が並列化できることです。

1ボリュームは数千個のPGの集合であり、各PGはフリート全体の異なるノードに散らばっています。もしノードAが落ちた場合、A上には数千個の異なるPGのセグメントが乗っているため、修復のソースもターゲットも数千ノードにバラけ、数千ストリームが並列に走って 修復が完了します。1つの大きな単位で複製していたら、ソースもターゲットも特定ノードに集中してボトルネックになっていたところです。

結果として、クォーラムを失うには「同じ10秒の窓のなかで2セグメント障害が発生し、しかもそれらを含まない別AZが丸ごと落ちる」という極めてまれな同時事象が必要になり、観測される故障率では十分に低い確率に抑え込めます。

整理すると、Auroraの耐障害性は次の二段構えで成立しています。

観点 担当する仕組み
「同時にいくつ落ちるか」を抑える(MTTF側) 3 AZ分散 + 6コピー + AZ+1クォーラム
「落ちた後どれだけ早く戻すか」(MTTR側) 10GBセグメント + Protection Group + 並列修復

新規性2: スマートストレージへのオフロード ―「ログこそがデータベース」

2つ目の新規性の主張を一言にまとめると、「データページを書くのをやめて、redo ログレコードだけをネットワークに流す。ページはストレージノードがログから自分で作る」 というものです。これによって、

  • ネットワーク帯域とDBノードのI/O負荷が桁違いに下がる
  • データページ書き出し・double-write・binlog といった従来の同期書き込み群がまるごと消滅し、コミットパスは「redoログを4/6ノードに永続化する」だけに痩せる
  • クラッシュリカバリ・チェックポイント・バックアップといった「重いバッチ処理」がストレージ層の継続的な背景処理に溶け込み、起動時の特別なリプレイが不要になる
従来の同期書き込みについて

double-write
InnoDBがページ書き込み中のクラッシュで起きる「torn page(半分だけ新しいページ)」を防ぐため、本来のデータ領域に書く前に専用領域へ同じページを書いておく仕組み。同じページを2回書くため書き込み増幅の代表例。
binlog(バイナリログ)
InnoDB内部のredoログとは別に、MySQLサーバ層が出力する論理的な変更履歴。レプリケーションへの転送とポイントインタイムリカバリ (PITR) に使われる。MySQLはコミット時に redo log と binlog の両方を永続化し整合性を取る必要があるため、コミットが重くなる原因にもなる。Aurora ではストレージ層がログからページを生成するので torn page 自体が発生せず double-write が不要、レプリケーションと PITR は分散ストレージ+S3バックアップで実現するため binlog も不要。

という効果が得られます。本章では以下の流れで掘り下げます。

  1. 従来の Mirrored MySQL がなぜI/O増幅で苦しむのか
  2. どう解決するか?Aurora における書き込みI/Oの新しい姿
  3. ログを受け取ったストレージノードが具体的にどう動くか
  4. 「スマート」たる所以。ダムなブロックストアでは不可能な、Aurora ストレージ固有の振る舞い

【課題】従来の MySQL のI/O増幅

MySQL を AZ をまたいで同期ミラーリングし EBS 上に乗せた構成を考えます。1回のアプリケーション書き込みが多種・多重の物理I/Oを生みます。

Figure 2: Network IO in mirrored MySQL(論文 §3.1 より)
Figure 2: Mirrored MySQL におけるネットワークI/O。画像中の LOG が redo log、BINLOG が binlog、DATA がデータページ、DOUBLE-WRITE が double-write バッファ、FRM FILES がテーブル定義メタデータを指す(FRMはMySQL 5.7までの概念で、8.0以降は廃止されている)。1回の書き込みがこれら複数段階を踏み、AZ間の同期ミラーリングでさらに増幅される。

"the redo log, the binary (statement) log that is archived to Amazon S3 ..., the modified data pages, a second temporary write of the data page (double-write) to prevent torn pages, and finally the metadata (FRM) files" (§3.1)

"Latency is additive because many writes are sequential. Jitter is amplified because, even on asynchronous writes, one must wait for the slowest operation" (§3.1)

加えて、これは分散的に見ると 4/4 write quorum となっており、外れ値障害(slowest node)に極端に脆弱です。

どう解決するか?: redo だけネットワークに流す

"In Aurora, the only writes that cross the network are redo log records. No pages are ever written from the database tier, not for background writes, not for checkpointing, and not for cache eviction. Instead, the log applicator is pushed to the storage tier where it can be used to generate database pages in background or on demand." (§3.2)

つまり log applicator(redo適用器)をストレージ層に押し込む のが本質です。実際のデータページ(レコードの変更)はストレージノード側がバックグラウンドまたはオンデマンドで生成します。

Figure 3: Network IO in Amazon Aurora(論文 §3.2 より)
Figure 3: Aurora のネットワークI/Oとクォーラム。DBエンジンから出るのは redo ログレコードだけで、3つのAZに分散した6つのストレージノードへ並列送信される。データページの書き出しやミラーリングはネットワークを越えない。

Aurora における書き込みI/Oフロー

Figure 4: IO Traffic in Aurora Storage Nodes(論文 §3.3 より)
Figure 4: ストレージノード内部のI/Oパイプライン。ログ受信 → インメモリキュー → ディスク永続化 → ACK返却(ここまでがフォアグラウンド)。その後、ギャップ識別、ピア間 gossip、ページ coalesce、S3 バックアップ、GC、CRC scrub は全てバックグラウンドで進む。

  1. [同期] DBエンジンが redo ログレコードを生成
  2. [同期] redo ログを、6つのストレージノード(前章のクォーラムで触れた、3 AZ × 各2コピーの複製先)へ並列に送信
  3. [同期] 各ストレージノードはログを受け取り、自ノードのインメモリキューに格納後、ディスクに書き込んで ACK を返す
  4. [同期判定] DBエンジンは 6つのうち 4つから ACK が返った時点でコミット成立 とみなし、クライアントに応答を返す
  5. [非同期] ストレージノードはバックグラウンドで、溜まった redo ログを実際のデータページに適用していく
  6. [非同期] ログとページを定期的に S3 にバックアップ
  7. [非同期] ノード同士でログの欠落をやり取りして補完したり、古いバージョンのGC、ディスク上データの整合性チェックなどを行う

Protection Group / gossip / coalesce / CRC scrub といった用語は、後の「ストレージノード内部の8ステップ」で改めて整理します。ここではまず「コミットを返すまでに必要なのは redo ログの永続化だけで、ページ生成・バックアップ・ノード間の同期は全部あとからゆっくりやる」という大枠を掴んでください。

ポイントは、フォアグラウンドパスが「redoログを4ノード分永続化するだけ」で完結することです。データページ書き出し、double-write、binlog、FRM など従来の同期I/Oは全て消滅しています。

比較結果

SysBench write-only, 100GB, r3.8xlarge × 30分:

Mirrored MySQL Aurora
Transactions 780,000 27,378,000
I/Os per transaction 7.4 0.95

6倍にレプリケートしているにもかかわらずDBノードあたりのI/Oは 7.7倍少なく、ストレージノード単体で見れば46倍少ないI/Oで済む計算になります。

なぜ「スマート」ストレージなのか

ダムなブロックストアにはできない、Aurora ストレージ固有の振る舞いをまとめます。

  • 継続的な redo 適用: クラッシュリカバリが通常運用に溶け込み、起動時の大規模リプレイが不要。
  • オンデマンド/背景でのページマテリアライズ: 長い変更チェーンを持つページのみ再生成対象になり、チェックポイントのように redo 全長に支配されない。
  • ピア間 gossip によるクォーラム穴埋め: 4/6クォーラムで書き込みを先に進めた結果、一部ノードに生じるログの欠落を、ストレージノード同士が後から補完。DBエンジンを経由しないため、DBエンジン側のI/Oや再送コストが発生しない。
  • 負の相関を持つ背景処理: 同期処理が忙しい時はGC等を遅らせ、CPU と ディスクをトレードして 99%レイテンシを守る。
  • 連続的な S3 バックアップもストレージ層自身が担う。

新規性3: ログの進行だけで一貫性を保つ(合意プロトコルの回避)

なぜ従来の合意プロトコルが問題だったか

従来の分散DBは、書き込みやコミット時に 2PC(2相コミット)や Paxos のような合意プロトコルを使っていました。これらは論文で「おしゃべり(chatty)」と形容されます。要するに

  • 「書きますよ(prepare)」「OK」「じゃあコミットします(commit)」「了解」のように、関係する全ノードと何度もネットワーク往復を繰り返す
  • 全員(または多数決)の返事を待って初めて次に進めるので、一番遅いノードに全員が引きずられる
  • ノード故障時に宙ぶらりんのトランザクションが発生するなど、障害処理も複雑

分散させるほどネットワーク越しのやり取りが増えてレイテンシが悪化する、というジレンマを抱えていました。

Aurora の解法「今どこまで確定したか」を示す数値を全員で進める

Auroraは、合意プロトコルのかわりに 「ログの書き込みがどこまで進んだか」を示す単調増加の数値マーカ を定義し、関係者がそれを基準に独立に判断する、というスキームを採用しました。合意のラウンドトリップは一切ありません。

この章では、そのマーカの正体と、Write・Read・レプリカ・リカバリのそれぞれで何が起きるかを追っていきます。

複数の指標が出てきますが、最初に押さえるべきなのは VDL (Volume Durable LSN) です。VDLはDBエンジン(writer)が管理する値で、ひと言で言えば:

VDL = 「今、このボリュームで公式にコミット済みと言える最新点」

ログレコード1つ1つに振られる通し番号を LSN (Log Sequence Number) と呼びますが、VDLは「そのLSNのうち、ここまではコミット完了済みとして読んでもコミット応答を返してもよい」という合意済みラインのことです。

Auroraの主要な判断はすべてVDLを基準に下されます。

場面 VDLの使い方
コミット確定 VDL ≥ commitしたトランザクションのLSN になったらクライアントにACK
読み取り開始 そのときのVDLを read-point として固定
バッファ追い出し 追い出されるページのLSN ≥ VDL であることを保証
レプリカのredo適用 LSN ≤ VDL のログだけ適用
クラッシュリカバリ 起動時に各セグメントからVDLを再計算
書き込みの背圧 未コミット分が大きくなりすぎたら書き込みをストール

VDLを軸にして、他の指標はそれを支える脇役 と捉えると見通しがよくなります。

SCL (Segment Complete LSN)

VDL はDBエンジンが管理するグローバルな値でした。これと対になるのが、各ストレージノードがローカルに持つ値 SCL (Segment Complete LSN) です。

SCL = 「このセグメントは、ログをLSN Xまで連続して受け取って永続化した」を表す値

各ストレージノードは自分の担当セグメントごとにSCLを管理しており、ログが届いてディスクに書けた時点でこの値が前進します。DBエンジンは各ノードからのACKやハートビート経由でSCLの値を把握しており、「どのノードがどこまで追いついているか」を知っています。

つまり:

誰が持つ スコープ 意味
VDL DBエンジン(writer) ボリューム全体 今、公式にコミット済みと言える最新点
SCL 各ストレージノード セグメント単位 このセグメントが受け取り終えた最大LSN

「グローバルな合意点としてのVDL」と「ローカルな追いつき具合としてのSCL」 の二つを押さえれば、以降のWrite/Read/レプリカ/リカバリの挙動はすべて説明できます。

構造を図にすると次のようになります。

書き込み時に起きること

VDLの前進を理解するために、その手前の2つの補助的な値を紹介します。

  • VCL (Volume Complete LSN): ボリューム全体で「クォーラム (4/6) を満たしたログがここまで揃っている」最大LSN。DBエンジンが保持し、ACKを集計して前進する。
  • CPL (Consistency Point LSN): ミニトランザクション (MTR) の終端マーカ。「ここで切れば整合が取れる」点を表す。DBエンジンが保持(ログ生成時にマークされる)。

VCLは「ログが揃っている最大点」、CPLは「整合的に切れる点」。VDLは VCL以下で最大のCPL として定まります。つまり「ログが揃っていて、しかも中途半端なMTRを含まない最新点」がVDL。

この前提のもと、前節「新規性2」で見た書き込みフローを指標の動きで解釈し直すとこうなります。

  1. DBエンジンがredoログを生成し、LSNを採番
  2. 6つのストレージノードへ並列送信(冗長性)
  3. 各ノードがログを永続化し、自ノードのSCLを前進 → ACKを返す
  4. DBエンジンが 4/6個のACKを検出 → VCL前進
  5. 直前のMTRの終端(CPL)がVCL以下にあれば VDL前進
  6. コミット処理は非同期グループコミット:
    • ワーカースレッドはコミット要求を受けると、対象トランザクションのLSNを「待機リスト」に入れて即座に他の処理へ移る(ブロックしない)
    • 専用スレッドがVDLの前進を監視し、VDL ≥ 待機中LSN になったものをまとめて検出し、クライアントへACK

ポイントは、ワーカースレッドがコミットを待たないこと。遅いノードを待っている間もCPUを別の処理に回せるため、スループットが跳ね上がります。

シーケンスで見るとこうなります。

読み取り時に起きること

読み取り時は「クォーラム読み」をしません。DBエンジンが持つ情報だけで1ノードを選びます。

  1. read-point固定: クエリ開始時の現在VDLを読み取り基準として保持
  2. ページ特定: InnoDBのB+Tree探索でアクセス対象ページ番号を算出(「前提: Auroraが差し替えたのは既存のDBのどこか」参照)
  3. バッファキャッシュ確認: ヒットすればそのまま利用
  4. キャッシュミス時:
    • ページオフセットから所属PG(Protection Group)を決定
    • PGの6つのセグメントのうち SCL ≥ read-point を満たすものから、最もレイテンシが良いセグメント1つを選ぶ
    • そのセグメントを持つストレージノードにページ要求を送信
    • ストレージノードは手元のredoをread-pointまで適用したページを返す

書き込みは6つのノード全員に送るが、読み取りは1ノードだけ — このアシンメトリがAurora流の効率化の要。

1つのクエリが複数のPGをまたぐ場合(例: フルスキャン)でも、全PGに同じread-pointを渡すので、一貫したスナップショットが得られます。各PGは独立に最適なセグメントをSCLを参照し選べるので、PG単位で並列化されます。

シーケンスで見るとこうなります。

レプリカ: キャッシュ同期だけで成立するレプリケーション

Auroraの読み取り専用レプリカは、従来のMySQLレプリケーションとは本質的に異なります。

従来MySQL
WriterとReplicaが別々のストレージを持ち、binlog を送って Replica 側で SQL を再実行し、同じ状態を再現する。ストレージ I/O もログ再生コストもレプリカ数だけ倍増。

Aurora
ライターとレプリカ が同じ分散ストレージボリュームを共有している(レプリカはストレージに何も書かない)。やることは自分のバッファキャッシュだけを Writer と同期させること。

ライターがストレージに送るredoログストリームは、レプリカにも並行して流れます。レプリカの挙動は2つの制約つきでシンプルです。

  • 対象ページがバッファキャッシュにある → redoを適用してキャッシュを更新
  • キャッシュにないログを破棄(次にそのページが必要になったら、共有ストレージから取り直せばredo適用済みのものが返る)
  • 適用は LSN ≤ VDL のログに限定する(公式コミット点を超える先行適用をしない)
  • 同一MTRのログは原子的に適用する(中途半端な状態を見せない)

レプリカでクエリが来たら、ライターと同じread-pointロジックで共有ストレージに読みに行きます。書き込みは無く、読み取り経路だけがWriterと共通です。

通常のレプリカラグは 20ms以下 に収まります。理由は明快で、ストレージへの書き込みが無く、ログを永続化する必要もない、やるのはキャッシュ内のページへのredo適用だけだからです。

クラッシュリカバリが「瞬時」になる理由

従来のDB(ARIES系)では、クラッシュ後の起動時に「チェックポイント以降のredoを前景同期適用」しないと使える状態にならず、これが起動時間の大半を占めます。

Auroraは違います。redo適用はストレージ層で常時・分散・非同期に行われているので、DBエンジンの再起動時に大規模なリプレイは不要です。起動時の処理は、

  1. 各PGの read quorum (3/6) を集めて VDL を再計算
  2. VDLを超える範囲を truncate range として記録(エポック番号付きで)
  3. undoはオンライン化したあとで処理

これにより、100,000 write/sec の最中にクラッシュしても通常10秒以下で復旧できるとされています。

ストレージノード間 gossip による自己修復

書き込みは4/6 ACKで先に進むため、個々のストレージノードは時折ログバッチを取りこぼします。これを DBエンジンを介さず、PG内のピアノード同士で補完 する仕組みが gossip です。各ノードは自分のSCLをピアと交換し、欠落ログを相互に送り合います。

結果、DBエンジンは一度送ったログを再送する必要がなく、一時的な遅れはストレージ層内で勝手に収束します。「自己修復ストレージ」の実体はこれです。

新規性3まとめ

Auroraは2PCやPaxosのようなおしゃべりな合意プロトコルを全面的に排除し、かわりに、

  • VDLという単一の「今の公式コミット点」を前進させる
  • 各スレッド・ノードはVDLを見てローカルに判断する(合意ラウンドトリップ不要)
  • ログ補完・ページ適用・バックアップはストレージ層の背景処理として常時走る
  • レプリケーションはキャッシュ同期だけで成立
  • クラッシュリカバリはVDL再計算だけで完了

この「ログの進行が一貫性・耐久性・可用性の全てを駆動する」設計が、Auroraの高スループット・低レイテンシ・高速リカバリを支えています。

どんなアプリケーションで効くのか?Write/Read別の整理

ここまで見てきた3つの新規性を踏まえて、どんなワークロードでAuroraが特に強みを発揮するかを整理します。結論を先に言うと、Write・Readのどちらにも効くが、効き方が違います

  • Write側: 論文のベンチマーク(§3.2 Table 1)で 従来のMySQLと比較して35倍のスループット・7.7倍のI/O削減と劇的な改善を示しています。
  • Read側: 最大15台のレプリカを低コストで水平スケールでき、実運用でのインパクトが大きい部分

Write-heavy で効く理由

論文の定量効果が特に劇的に出るのはこちらです。

  • I/O増幅の解消: 従来は redo / binlog / データページ / double-write / FRM と 7〜8 種類の書き込みが発生していたが、Aurora では redo ログレコードだけがネットワークを越える(新規性2)
  • 非同期グループコミット: ワーカースレッドはコミット完了を待たず、別スレッドが VDL の前進を監視して一括で ACK を返す(新規性3)。遅いノードを待っている間もCPUを他の処理に回せる
  • 4/6クォーラム: 遅い 2 ノードを待たずに書き込み確定できるので、外れ値ノードに引きずられず p99 レイテンシが安定する(新規性1・2)
  • double-write / binlog / チェックポイントが不要: コミットパスが痩せているので、高TPS下でも破綻しにくい

つまり、高頻度な小さな更新(典型的な OLTP) ほど Aurora の優位が数字として効きやすい。

Read-heavy で効く理由

こちらはアーキテクチャ上の帰結として強みになります。

  • 最大15台の読み取り専用レプリカ: Writer とストレージを共有するため、レプリカを増やしてもストレージコストが比例して増えない
  • レプリカラグが 20ms 以下: キャッシュ同期だけで済むため、ほぼ最新の状態を読める
  • クォーラム読みをしない: 読み取りは 6 ノードのうち 1 ノードへ直接問い合わせるだけ(新規性3)。往復が1回で済むのでレイテンシが低い
  • 各レプリカは独立したバッファキャッシュを持つ: 読み取りワークロードをレプリカの台数分だけ水平スケールできる

つまり、「書き込みはそこそこで読み取りが非常に多い」Webサービス・API バックエンド・ダッシュボード系 で Aurora のコストパフォーマンスが強く効きます。

向いているユースケース

ユースケース Aurora が効く理由
金融系トランザクション(送金・決済) 高 TPS + 高耐久 + 低ジッタ。Write-heavy かつ p99 重視
SNS / EC のバックエンド 更新:参照 = 1 : N の構成で、読み取りをレプリカで水平スケール
リアルタイムダッシュボード 書き込み直後に見られ、レプリカラグが小さい
マルチテナント SaaS 共有ストレージによるレプリカ増設のコスパが良い

Auroraの制約

ここまで良い面ばかりを見てきましたが、Auroraは万能ではありません。採用判断にあたって押さえておくべき制約を3点に絞って紹介します。

1. Single-writer アーキテクチャ(書き込みは水平スケールしない)

Auroraは クラスタ内にWriterが1台だけという構成です。レプリカは最大15台まで増やせますが、すべて読み取り専用。

  • 書き込みスループットは Writer 1 台の CPU/メモリ上限に張り付く。ストレージ側がいくら分散・並列化されていても、エンジン側は1インスタンス
  • Writer 障害時にはフェイルオーバーが走り、その間は書き込み不可になる
  • 書き込みを水平スケールさせたいワークロードには根本的に不向き。その場合は TiDB / CockroachDB / Spanner のような分散DBを検討する必要がある

2. クロスリージョンの強整合性は提供されない

Auroraの 6 コピー / 3 AZ 構成は 1 リージョン内で閉じています。リージョン障害に備えたい場合は Aurora Global Database を使いますが、これは非同期レプリケーションです。

Aurora は「1 リージョン内での OLTP エンジン」としては強いですが、地理分散の強整合性が欲しい場合はSpannerなどを検討すると良いでしょう。

3. クエリ並列実行(MPP)は持っていない

1クエリは1つのエンジンインスタンスで実行されます。大規模集計や複雑なJOINを並列化する仕組みはありません。OLAPワークロードには BigQuery / Redshift / Snowflake のようなMPP型のDWHのほうが適しています。

上記の制約を踏まえて向かないケースを例示しました。

向かないユースケース

ユースケース 向かない理由
OLAP / 大規模集計クエリ 1 クエリは 1 エンジンインスタンスで実行される。MPP 型の並列実行ではない。BigQuery / Redshift / Snowflake が適切
I/O 負荷が低い小規模ワークロード 6 コピー冗長化のオーバーヘッドが相対的に割高
コスト最小化を最優先、耐久性要件がゆるい 通常のRDS (MySQL/PostgreSQL) のほうが安く済む
ログ書き溜め・キューのような単純な用途 DynamoDB / SQS / S3 のほうが適合する

まとめ

Aurora は「Write のネットワークI/O の増幅問題を根本から解消し、Read はレプリカで水平にスケールする」という形で、OLTP ワークロード全般で既存の MySQL に対して大きな優位を持ちます。一方、OLAP や小規模ワークロードではアーキテクチャ上の前提が噛み合わないため、用途ごとに Redshift / BigQuery / RDS / DynamoDB などと使い分けるのが現実的です。

Discussion