Amazon Web Services ブログ

Amazon Aurora Multi-Master を使用して高度に使用可能な MySQL アプリケーションによりビルド

アップタイム要件が高いトランザクション性のアプリケーションをお持ちですか? これらの要件を満たすために、クラウドにリレーショナルデータベースが必要ですか? 新しく開始された Amazon Aurora Multi-Master は、ノードの障害に柔軟性のあるリレーショナルデータベースを必要とするアプリケーションのために設計され、読み取りと書き込みの両方に利用可能です。

Amazon Aurora は、ハイエンドな商用データベースの速さや可用性と、オープンソースデータベースのシンプルさと高い費用対効果とを組み合わせたリレーショナルデータベースエンジンです。MySQL 互換エディションの Aurora は、同じハードウェアで動作する標準 MySQL の最大 5 倍のスループットを実現します。これにより、既存の MySQL アプリケーションおよびツールを変更することなく実行できます。Amazon Aurora は 1 個のライターと最高 15 個の読み取りレプリカを 1 つ以上のアベイラビリティ―ゾーンとリージョンでサポートします。

Amazon Aurora Multi-Master は Aurora の MySQL 互換エディションに使用可能です。クラスターの各ノードはライターノードで、厳しいアップタイム要件をもつトランザクション性アプリケーションを実行するための追加パワーを与えます。

この記事では、MySQL 互換エディションのデータベース用の Aurora Multi-Master を最大限利用するために知っておくべきことを見直します。

アーキテクチャ

Aurora Multi-Master は、データベースノードのクラスター全体で高可用性と ACID トランザクションを実現し、読み取り / 書き込み後の整合性を設定できるように設計されています。そのコアで、Aurora クラスターは計算 (データベース) ノードと共有ストレージボリュームのセットから構成されています。ストレージボリュームは、ユーザーデータの高可用性と耐久性のために、3 つのアベイラビリティーゾーンに配置された 6 つのストレージノードで構成されています。クラスターの各データノードは、ステートメントの読み取りと書き込みを実行できるライターノードです。

下図は 2 ノードの Aurora Multi-Master クラスターの論理ビューを示します。

クラスターに 1 つのポイントの障害もないダイアグラムあ表示されます。アプリケーションは、読み取り/書き込みと DDL ニーズのための任意のライター ノードを使用できます。ライターノードにより行われたデータベースの変更は、3 つのアベイラビリティ―ゾーンの 6 つのストレージノートに書き込まれ、ストレージノードとアベイラビリティゾーンの障害に対して、データの耐久性と柔軟性を提供します。ライターノードは、すべての機能性が同等で、1 つのライターノードの失敗は、クラスターのほかのライターノードのアベイラビリティーには影響しません。

レプリケーションとクォーラム

アプリケーション層がトランザクションを開始し、データベースを変更すると、その変更を処理するライターノードは 6 個すべてのストレージノードへの変更を提案することで、クロスクラスターの整合性を達成します。提案された変更が以前コミットした変更と競合するかどうかを各ストレージノードに対してチェックして、変更を確定するか、拒否します。

変更を提案したライターノードがストレージノードのクォーラムからポジティブな確認を受け取った場合、次の 2 つのことを行います。最初に、ストレージ層の変更をコミットし、各ストレージノードに変更をコミットさせます。次に、低レイテンシーのピアツーピアのレプリケーションプロトコルを使用して、クラスターで 1 つおきにライターノードに変更レコードをレプリケートします。変更を受け取った時点で、ピアライターノードが変更をメモリ内のキャッシュ (バッファプール) に適用します。

変更を提案したライターノードがストレージノードのクォーラムからポジティブな確認が得られない場合、トランザクション全体を取り消して、アプリケーション層にエラーを発生させます。その後、アプリケーションはトランザクションを再試行できます。

実施の観点から、ライターノードはページに変更を提案します。ページは行のセットを含むメモリのブロックで、サイズは 16 KB です。ページには ID とログシーケンス番号 (LSN) があります。ライターノードは、ストレージ層に REDO 変更レコードを送信することで、ストレージノードのページを更新します。

変更をストレージ層に正常にコミットすると、ライターノードは REDO 変更レコードをピアのライターノードにレプリケートして、バッファプールにピアノードを更新させます。バッファプール、ページの詳細と、InnoDB ストレージエンジンの一般的な理解については、InnoDB ストレージエンジンのドキュメント を参照してください。

高可用性

Aurora Multi-Master は、クラスターのノードのすべてが読み取り / 書き込みノードであるため、Amazon Aurora の単一マスタバージョンの高可用性を向上させます。

単一マスタ Aurora により、単一のライターノードの障害では、読み取りレプリカを新しいライターに昇格させることが必要になります。Aurora Multi-Master の場合、ライターノードの障害では、ライターを使用するアプリケーションに別のライターへの接続を開かせることだけが必要です。

アプリケーションをライターに適切に割り当てることにより(競合の回避のセクションを参照)、またライターの障害が検出されたら正常なライターに接続を再配布することにより、アップタイムを長くするように調整することができます。高可用性のために設計するときは、ライターに負荷をかけすぎないようにしてください。アプリケーションでライターノードのヘルスチェックを実施し、接続を障害のあるライターから健全なライターに移動します。

Java のような言語でこれを行う 1 つの方法は、データベース接続を管理するシングルトンクラスを実装することです。このクラスは、ヘルスチェックをカプセル化できます。このくらすはまた、アプリケーションやアプリケーションコンポーネントにライターノードをマッピングするライター割り当てマップも維持できます。これにより、同時ライターが重複しない更新を処理し、競合を回避できるようになります (競合を生じさせることができるシナリオについては、次のセクションを参照してください)。ライターの選択を心配せずに、シングルトンクラスから接続をリクエストするだけです。マルチスレッド環境での接続の問題を防止するための適切なガードレールを使って、このクラスがスレッドセーフであることを確認してください。

下図は、ライターノードの障害字にアプリケーションティアにより接続が再配布される方法とその後のリカバリが継続的なデータベースの可用性を提供する様子を示します。図 1 は Writer 1 と Writer 2 に確立されたデータベース接続と共にアプリケーションを表示します。図 2 は、Writer 1 の障害を示し、クラスターからそれを取り出します。アプリケーションは障害のある Writer 1 と Writer 2 にあった接続を再確立 (移動) します。図 3 は Writer 1 が修理され、オンラインに戻る様子を示しています。アプリケーションは Writer 2 を Writer 1 に移動させた接続を移動します。

図 1

図 2

図 3

競合管理

同時トランザクションまたは書き込みが別のライターノードで実行中の時に、同じセットのページを変更しようとすると競合が発生します。分散システムでの競合の解決では、システム全体で発生するイベントの因果関係と順序付けを確立するための確実なメカニズムが必要です。

多くのシステムは、パフォーマンスを犠牲にして競合を検出して、回避するために書き込みを集中管理します。そのようなシステムで、書き込みを受け付ける各データベースノードは書き込みを中央マスタに転送し、承諾を待つ必要があります。書き込みの並行性が達成される一方で、中央の書き込みマスタがパフォーマンスのボトルネックになる可能性があるため、ソリューションには問題が多く生じます。

 Aurora Multi-Master の競合検出は分散され、集中管理されたエージェントがデータベースへの書き込みのプロキシに責任をもつシステムで見られるパフォーマンスの問題に悩まされていません。ライターはストレージノードにページへの変更を提案します。各ストレージノードはライターノードによって送信されたページの LSN(これをページバージョンと考える)を、ノード上のページの LSN と比較します。それらが同じ場合は変更を承認し、ストレージノードにページの最新バージョンが含まれている場合は競合を伴う変更を拒否します。

複数のライターはページへの変更を同時に提案できます。承認のクォーラムを受け取る最初のライターが勝利し、そのトランザクションを続行できます。その他の敗れたライターは変更を打ち切り、トランザクションをロールバックさせます。ライターのトランザクションはページの行を変更し、ストレージノードはページ全体を比較して競合を検出します。行が同じページにある場合、テーブルの2 つの異なる行を変更している 2 つの同時ライターが引き続き競合している可能性があります。競合を回避するためのアプリケーションを設計するときは、いくつかのベストプラクティスに従うことをお勧めします。

  • 同時ライターからの重複ページの更新を実行しないでください。シャーディングされたデータベースがある場合は、ライターをシャードに割り当て、割り当てられたライターを通じてシャードを更新することをお勧めします。マッピングはアプリケーション層でのみ論理的です。物理的に、ストレージボリュームのデータは、すべてのライターノードに見えるようになります。複数のシャードにわたるトランザクションは、単一のライターから引き続き実行できます。
  • アプリケーション層へのデータベースノードにより競合が生じるとき、トランザクションをやり直してください。指数関数的バックオフなどの手法により、バッファープールがレプリケーションに追いつき、トランザクションが接触したページの最新の変更を反映するための時間が与えられ、成功の可能性が増します。
  • 独自のアプリケーションの設計とニーズに基づいて、許容可能な書き込み競合率、同等のライター使用率、および可能な限り最高の可用性を実現する方法で、クエリをライターにルートします。

競合を作るシナリオのいくつかの例は、次のとおりです。Aurora Multi-Master はストレージ層が競合を検出し、変更を拒否すると、MySQL エラー 1213 (deadlock) を返します。以下のシナリオは、MySQL CLI と 2 ノードの Aurora Multi-Master クラスターを使用してシミュレーションされます。

シナリオ 1: 隣接する行

2 つのライターが同時に、表 11 の隣接する行を更新しています。これらの行は、InnoDB により同じページに保存されています。2 つの同時トランザクションにより、ストレージ層に物理的な競合が作られ、デッドロックエラーでセッションの 1 つが中断されます。

###### SESSION 1 from Writer 1 

mysql> select @@aurora_server_id;
+-------------------------+
| @@aurora_server_id      |
+-------------------------+
| multimaster-test-mm-1-1 |
+-------------------------+
1 row in set (0.00 sec)

mysql> select * from t1;
+----+------+
| id | s1   |
+----+------+
|  1 | a    |
|  2 | b    |
|  3 | c    |
|  4 | d    |
|  5 | e    |
+----+------+
5 rows in set (0.00 sec)

mysql> select now(3); update t1 set s1 = 'x' where id = 1;
+-------------------------+
| now(3)                  |
+-------------------------+
| 2019-05-17 17:25:24.904 |
+-------------------------+
1 row in set (0.00 sec)

Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

###### SESSION 2 from Writer 2 

mysql> select @@aurora_server_id;
+-------------------------+
| @@aurora_server_id      |
+-------------------------+
| multimaster-test-mm-1-2 |
+-------------------------+
1 row in set (0.00 sec)

mysql> select * from t1;
+----+------+
| id | s1   |
+----+------+
|  1 | a    |
|  2 | b    |
|  3 | c    |
|  4 | d    |
|  5 | e    |
+----+------+
5 rows in set (0.01 sec)

mysql> select now(3); update t1 set s1 = 'y' where id = 2;
+-------------------------+
| now(3)                  |
+-------------------------+
| 2019-05-17 17:25:24.905 |
+-------------------------+
1 row in set (0.00 sec)

ERROR 1213 (40001): ロックを取得しようとしたときに、デッドロックが見つかりました。トランザクション  を再開しようとしています

シナリオ 2: 同時に同じ行

2 つのライターが同時に、表 11 の隣接する行を更新しています。2 つのセッションが同じライターから実行されている場合、セッションの 1 つで論理的な重複、ロック待ち状態、そして最終的にはロック待ちタイムアウトが生じます。

Aurora Multi-Master では、2 つのセッションが 2 つの異なるライターから同時に実行される場合、デッドロックエラーでセッションの 1 つが中断されます。以下のシナリオでは、Writer 1 がトランザクションを開始し、テーブル 11 の行を更新し、トランザクションをコミットする前に、Writer 2 がテーブル 11 の同じ行を更新しようとしたため、デッドロックエラーで失敗しています。

###### SESSION 1 from Writer 1

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select now(); update t1 set s1 = 'z' where id = 1;
+---------------------+
| now()               |
+---------------------+
| 2019-05-17 17:28:04 |
+---------------------+
1 row in set (0.00 sec)

Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

###### SESSION 2 from Writer 2

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select now(); update t1 set s1 = 'v' where id = 1;
+---------------------+
| now()               |
+---------------------+
| 2019-05-17 17:28:14 |
+---------------------+
1 row in set (0.00 sec)

ERROR 1213 (40001): ロックを取得しようとしたときに、デッドロックが見つかりました。トランザクションを再開しようとしています

シナリオ 3: 同じ行で少しずらす

2 つのライターが互いに数秒ずらして、同じ行を更新しています。このシナリオでは、セッション 1 がテーブル 11 で id = 1 の行を変更する更新ステートメントを発行し、暗黙に変更を自動コミットします。数行後に、Writer 2 がテーブル 11 の同じ行の更新を試みます。両方のトランザクションは。クラスターのレプリケーションに対して、書き込みの間に追いつくための十分な時間があるため、競合なしで成功しています。

###### SESSION 1 from Writer 1

mysql> select now(); update t1 set s1 = 'z' where id = 1;
+---------------------+
| now()               |
+---------------------+
| 2019-05-17 17:29:03 |
+---------------------+
1 row in set (0.00 sec)

Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

###### SESSION 2 from Writer 2

mysql> select now(); update t1 set s1 = 'v' where id = 1;
+---------------------+
| now()               |
+---------------------+
| 2019-05-17 17:29:07 |
+---------------------+
1 row in set (0.00 sec)

Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

グローバルな書き込み後読み取りの整合性

読み取りの整合性とは、書き込みと読み取りに使用されているノードに関係なく、読み取りが最新のコミットされた変更の状態を反映することです。ほとんどの実装では読み込みがデータベースに書き込んだノードと同じノードで発生する場合、書き込み後の読み込みの整合性があります。書き込みのすぐ後でほかのノードに発生する読み込みは、レプリケーションの遅れによっては、数ミリ秒間、書き込みの変更がひょうじされない場合があります。

Aurora Multi-Master は、デフォルトのノードレベルの整合性モードまたはグローバルな書き込み後読み取り(GRAW)と呼ばれるクラスター全体の整合性モードを柔軟に選択できます。GRAW により、アプリケーションは最新のデータの上に、クラスター全体で一貫した読み取りを行うことができます。読み取りパフォーマンスの点ではわずかなペナルティがありますが、読み取りの強い整合性が必要なアプリケーションの場合は、許容できるトレードオフです。

デフォルトモードで、書き込み後読み取りの整合性は、読み取りが同じライターノードで行われる場合には保証されます。ほかのノードは、数ミリ秒 (レプリケーションの遅延) 居ないで書き込みの状態をい反映します。

まとめ

MySQL 互換エディション用の Aurora Multi-Master は、堅牢な競合管理をもつ複数のライターを必要とする MySQL のワークロードの素晴らしいソリューションになりえます。また、それは高いアップタイム、Active-Active デプロイなどのアプリケーションシナリオ、および 1 テナントごとに 1 つのデータベースをもつ複数テナントのデータベースを実行しなければならないときなどの SaaS プロバイダー使用事例もサポートします。

開始時に、Aurora Multi-Master は単一リージョン内で 2 つのノードクラスターをサポートします。その他のライターノードや複数リージョンにおけるライターの配置に関するサポートは、今後のリリースで予定されています。

 


著者について

Mukund Sundararajan はアマゾン ウェブ サービスのソリューションアーキテクトです