CubicLouve

Spring_MTの技術ブログ

Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databasesを読む(その4の補足資料)

MySQLは8.0.19を使っています。

LSN

LSNとは Log Sequence Number の略で、単調増加する値で、redoログに書き込まれたバイト数分だけLSNが増えます。

dev.mysql.com

MySQLでは SHOW ENGINE INNODB STATUS でLSNを確認できます。

---
LOG
---
Log sequence number          137326682480
Log buffer assigned up to    137326682480
Log buffer completed up to   137326682480
Log written up to            137326682480
Log flushed up to            137326682480
Added dirty pages up to      137326682480
Pages flushed up to          137326682480
Last checkpoint at           137326682480
13 log i/o's done, 0.00 log i/o's/second

これらの値は、下記で定義されています。

github.com

INSERTするときのLSNが増える様子を示します。

SQL LSN comments
---
LOG
---
Log sequence number 137326682480
Log buffer assigned up to 137326682480
Log buffer completed up to 137326682480
Log written up to 137326682480
Log flushed up to 137326682480
Added dirty pages up to 137326682480
Pages flushed up to 137326682480
Last checkpoint at 137326682480
13 log i/o's done, 0.00 log i/o's/second
BEGIN; ---
LOG
---
Log sequence number 137326682480
Log buffer assigned up to 137326682480
Log buffer completed up to 137326682480
Log written up to 137326682480
Log flushed up to 137326682480
Added dirty pages up to 137326682480
Pages flushed up to 137326682480
Last checkpoint at 137326682480
13 log i/o's done, 0.00 log i/o's/second
BEGINではLSNは増えない
INSERT INTO innodb_auto_increment_small VALUES(); ---
LOG
---
Log sequence number 137326682671
Log buffer assigned up to 137326682671
Log buffer completed up to 137326682671
Log written up to 137326682671
Log flushed up to 137326682671
Added dirty pages up to 137326682671
Pages flushed up to 137326682671
Last checkpoint at 137326682671
17 log i/o's done, 0.36 log i/o's/second
COMMIT; ---
LOG
---
Log sequence number 137326682878
Log buffer assigned up to 137326682878
Log buffer completed up to 137326682878
Log written up to 137326682878
Log flushed up to 137326682878
Added dirty pages up to 137326682878
Pages flushed up to 137326682878
Last checkpoint at 137326682878
19 log i/o's done, 0.13 log i/o's/second

トランザクション中でもLSNは増加します。

このあたりはredoログの実装周りを見ればよいかなと思います。

dev.mysql.com

MTR

ごく僅かな一連のデータベージへの変更を記録したもの。

redoログは512バイトのブロックの連続で、個々のMTRはブロックをまたいで記録されることもある。

dev.mysql.com

(詳解 MySQL5.7 p 126)

AuroraのLSN関連の概念

Name Full Name description
VCL Volume Complete LSN すべてのログレコードの可用性を保証できる一番大きいLSN
SCL Segment Complete LSN セグメントの中の最大のLSN、セグメントの穴を探すのに使われる
PGCL Protection Group Complete LSN PG内の最大のLSN
CPLs Consistency Point LSNs 一つのMTRのなかの最後のログレコードのLSN
VDL Volume Durable LSN VCLより小さいが最も大きいCPLMTRが完了したことを示している
PGMRPL Protection Group Min Read Point LSN データ読み込済みのPGごとの最小のLSN

ストレージの一貫性のポイントの例

f:id:Spring_MT:20210322162847p:plain

書き込み、Commitをする場合

f:id:Spring_MT:20210323040453p:plain f:id:Spring_MT:20210323040611p:plain

最後は、最新のVDLがトランザクションのコミットLSN以上になったのでクライアントにackを送る。

読み込み

f:id:Spring_MT:20210323162524p:plain

read replica

f:id:Spring_MT:20210323174517p:plain

Recovery

f:id:Spring_MT:20210324165018p:plain f:id:Spring_MT:20210324165046p:plain

Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databasesのまとめ

Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databases の翻訳まとめページ

https://www.allthingsdistributed.com/files/p1041-verbitski.pdf

翻訳内容

spring-mt.hatenablog.com

spring-mt.hatenablog.com

spring-mt.hatenablog.com

spring-mt.hatenablog.com

spring-mt.hatenablog.com

spring-mt.hatenablog.com

spring-mt.hatenablog.com

spring-mt.hatenablog.com

spring-mt.hatenablog.com

Auroraについて

mrasu.hatenablog.jp

qiita.com

www.slideshare.net

https://pages.awscloud.com/rs/112-TZM-766/images/B3-05.pdf

https://pages.awscloud.com/rs/112-TZM-766/images/B3-06.pdf

https://pages.awscloud.com/rs/112-TZM-766/images/D2-05.pdf

dev.classmethod.jp

www.slideshare.net

speakerdeck.com

www.slideshare.net

dev.classmethod.jp

aws.amazon.com

その他参考

www.slideshare.net

qiita.com

www.planetscale.com

speakerdeck.com

Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databasesを読む(その9 CONCLUSION)

9 CONCLUSION

Auroraは,クラウドの環境で、可用性も耐久性も妥協しない、高スループットのOLTPデータベースとして設計した。

この大きなアイデアは、伝統的なデータベースのモノリシックなアーキテクチャからの脱却と、ストレージをコンピュートの分離であった。

特に、ログやストレージを管理するデータベースの重要な部分の下位1/4を、独立したスケーラブルな分散型サービスに移行した。

すべてのI/Oがネットワーク越しに行われるため、主な制約はネットワークになった。

そのため、ネットワークの負担を軽減し、スループットを向上させる技術に注力した。

私たちは、下記に依存している。

  • クオラムモデル
    • 大規模なクラウド環境で発生する複雑で相関性のある障害をうまく処理し、外れ値のパフォーマンスペナルティを回避する
  • ログ処理
    • 集約されたI/Oの負担を軽減する
  • 非同期的な合意
    • 通信が多くでコストが掛かる多相同期プロトコル(XAとかのことかな)、オフラインでのクラッシュリカバリ、分散ストレージのチェックポイント作成を排除する

これらのアプローチは、複雑さを減らしたシンプルなアーキテクチャを実現し、拡張性に優れているだけでなく、将来の進歩のための基盤となっている。

Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databasesを読む(その7 LESSONS LEARNED)

7 LESSONS LEARNED

ここでは、クラウドで一般的なシナリオとそれに求める期待にフォーカスして、新たな方向性を導き出す。

7.1 マルチテナンシーとデータベースの統合

AWSのユーザーの多くは、SaaSビジネスを展開している。

SaaSでは、一般的にschema/databaseをテナントの単位として、異なる顧客を単一のインスタンスにまとめている。

すべての顧客が一度にアクティブになることはないので、顧客ごとに専用のインスタンスを購入する必要がないので、コストとの削減ができている。

(このモデルは、Salesforce.com のような有名なマルチテナントアプリケーションとは大きく異なる。Salesforce.comでは、マルチテナント・データ・モデルを使用し、複数の顧客のデータを単一のスキーマで統一されたテーブルにまとめ、テナントは行ごとに識別される。)

その結果、多数のテーブルを含むデータベースになる。

小規模なデータベースであっても、150,000テーブルを超える場合もよくある。

これは、dictionaryキャッシュなどのメタデータを管理するコンポーネントに負担がかかる。

さらに重要なのは、このようなお客様が必要としているのは、

  1. 高レベルのスループットと多くの同時接続を維持すること
  2. データが使用されたときにのみプロビジョニングされ、支払いが行われるモデル、
  3. ジッターの低減( 一つのテナントのスパイクが他のテナントに与える影響を最小限になる)

Auroraはこれらをサポートしており、このようなSaaSアプリケーションに非常に適している。

7.2 同時接続が多い状態でのオートスケーリング

突然の予期せぬイベントによるトラフィックのスパイクに対応する必要がある。

ある大手のお客様は、データベースに負荷をかけることなく、通常のピークのスループットを大幅に超えるスパイクが発生した。

このようなスパイクに耐えられるためには、データベースは同時接続の増大をうまくハンドリングすることが重要になる。

Auroraでは、基盤となるストレージシステムが非常にスケーラブルなので、このアプローチは実現可能である。

Auroraのユーザーの中には、毎秒8000以上の接続がある人もいる( per instanceだと思うけどインスタンスサイズの記載がない )

7.3 スキーマの進化

Ruby on Railsのような最新のWebアプリケーションフレームワークは、ORMと深く結合している。

その結果、アプリケーション開発者は簡単にかつ頻繁にデータベースのスキーマを変更できる。

MySQLは自由なスキーマ変更ができる仕組みを提供しているが、殆どのスキーマ変更の実装はテーブルのfullコピーが前提のため、スキーマの変更は辛いものである。

これは実際的な問題なので、Auroraでは効率的なオンライン DDL を実装した。

この実装は下記の通り

  1. ページ単位でスキーマをバージョン管理し、スキーマ履歴を使って個々のページを必要に応じてデコードする
  2. modify-on-write (書き込み時に修正するCoWのもじりかな。。。)を使用して個々のページを最新のスキーマに遅延アップグレードする

dev.classmethod.jp

blog.father.gedow.net

7.4 可用性とソフトウェアのアップグレード

ユーザーはクラウドネイティブなデータベースに高い期待を寄せていますが、それはAWSインスタンス群の運用やサーバーへのパッチ適用の頻度と相反することがある。

ユーザーはAuroraを主に本番アプリケーションを支えるOLTPサービスとして使用しているの、中断が発生するとそれはトラウマを引き起こす。

多くのユーザーは、たとえ6週間に1度30秒程度の計画ダウンタイムであっても、私たちが行うデータベースソフトウェアのアップデートに対してひどく脆弱である。

なので、AuroraではZDP(Zero Downtime Patch)機能をリリースした。

これにより、動作中のデータベース接続に影響を与えることなく、パッチを適用することができる。

図12に示すように、ZDPは、アクティブなトランザクションがない瞬間を探し、その瞬間にアプリケーションの状態をローカルのエフェメラルストレージに一時的に保存し、データベースエンジンにパッチを当てた後、アプリケーションの状態を再ロードするという仕組みになっている。

スプーリング - Wikipedia

このプロセスでは、ユーザー・セッションはアクティブなままで、エンジンが変更されたことに気がつくことはない。

f:id:Spring_MT:20210317174302p:plain

docs.aws.amazon.com

Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databasesを読む(その6 PERFORMANCE RESULTS)

6 PERFORMANCE RESULTS

2015年7月にGAとなったAuroraを本番環境で稼働させた際の経験を紹介する。

6.1 標準的なベンチマークでの結果

SysBenchやTPC-C 亜種などの業界標準ベンチマークを用いて、AuroraとMySQLの性能を比較する。

特に記載のない限り、32 vCPU、244 GBのメモリを持つ r3.8xlarge EC2インスタンス(インテルXeon E5-2670 v2 (Ivy Bridge) プロセッサー)を使い、30k IOPSのEBSボリュームがアタッチされているインスタンスを使う。

r3.8xlargeのバッファキャッシュは170GBに設定されてる

6.1.1 インスタンスサイズによるスケール

r3ファミリーの5つのEC2インスタンス(large、xlarge、2xlarge、4xlarge、8xlarge)で、1GBのデータセット(250テーブル)のSysBenchのread-only と write-only のベンチマークの結果。

インスタンスサイズに応じてCPU、メモリは倍々に増えていく。

旧世代のインスタンス - Amazon EC2 | AWS

この論文の時点では、AuroraはMySQL 5.6のコードベースとなっている。

f:id:Spring_MT:20210317105047p:plain

f:id:Spring_MT:20210317105110p:plain

Auroraのパフォーマンスは、インスタンスサイズが大きくなるごとに2倍になる、つまり、Auroraのスループットインスタンスサイズに応じてリニアにスケールすることがわかった。

r3.8xlargeでは121,000 write/sec、600,000 read/secを達成しました。

これは、r3.8xlargeでのMySQL 5.7の最高速度が20,000 read/sec、125,000 write/secであることと比較すると、大体5倍になる。

6.1.2 データサイズを変化させた場合のスループット

f:id:Spring_MT:20210317110740p:plain

SysBenchのwrite-onlyのベンチマーク結果。

データベースサイズが100GBの場合、AuroraはMySQLよりも最大67倍高速になる。

データベースサイズが1TBでキャッシュに乗り切らない場合でも、AuroraはMySQLよりも34倍高速である。

6.1.3 ユーザーのコネクション数のスケーリング

接続数が50→500→5000と増やしていく中での、SysBench OLTPベンチマークにおけるwrites/secの結果。

f:id:Spring_MT:20210317134035p:plain

Auroraが40,000 write/secから110,000 write/secまでスケールするのに対し、MySQLスループットは500接続前後でピークに達し、5000接続になると急激に低下する。

参考 : MySQL 8.0などの状況

yakst.com

6.1.4 レプリカのスケール

レプリ遅延についての結果。

レプリ遅延は、コミットされたトランザクションがレプリカで見えるようになるまでの時間で測定する。

f:id:Spring_MT:20210317140311p:plainf:id:Spring_MT:20210317140311p:plain

ワークロードが1,000から10,000 writes/secに増やしたときレプリ遅延をみる。

Auroraのレプリカラグは2.62 msから5.38 msに伸びる。

MySQLのレプリカラグは1000 ms以下から300000 msに延びる。

10,000 writes/secの状態では、AuroraのレプリカラグはMySQLのそれよりも数桁小さい。

6.1.5 行の競合が多い場合のスループット

TPC-C複雑な OLTP アプリケーションに対するベンチマーク

www.tpc.org

TPC-C All

https://japan.zdnet.com/glossary/exp/TPC-C/?s=4

sh2.hatenablog.jp

Perconaが提供しているTPC-C の亜種でベンチマークをとった。

github.com

f:id:Spring_MT:20210317142200p:plain

500 コネクション 10GB のデータから、 5000 コネクション 100GBのデータになるまでのスループットは、 MySQL 5.7と比較しAuroraは、2.3 倍から 16.3 倍のスループットがでる。

sh2.hatenablog.jp

6.2 実際のユーザーでの結果

6.2.1 Auroraでのアプリケーション応答時間

f:id:Spring_MT:20210317152650p:plain

とあるゲーム会社の例。

r3.4xlargeインスタンスMySQLからAuroraへの移行。

移行前のウェブトランザクションの平均応答時間は15msで、移行後の平均応答時間は5.5msへ。

6.2.2 Auroraでのレイテンシー

f:id:Spring_MT:20210317152854p:plain

f:id:Spring_MT:20210317152930p:plain

とある教育テクノロジー会社の例。 本番環境のワークロードをMySQLからAuroraに移行。

移行前のP95遅延は40 ms 〜 80 msの間で、P50の約1 msよりもはるかに悪い。

このアプリケーションでは、この論文の最初にあるような外れ値によるパフォーマンス低下の問題が発生していた。

移行後、SELECT、INSERT操作のP95は劇的に改善され、P50とほぼ同じになった。

(これそもそもDBの設定とかが悪い可能性もある)

6.2.3 複数のレプリカを使用した場合のレプリ遅延

MySQLのレプリカは、ライターに比べて大幅に遅延することが多く、PinterestのWeiner氏が報告しているように「変なバグを引き起こす可能性がある」とされている。 (単純に考慮もれとかじゃないのかなあ、、、)

medium.com

さきほどの教育テクノロジー会社では、レプリ遅延が12分になることもあり、アプリケーションの正確性に影響を与えるためレプリカはstand-byとしてしか使えていないかった。

Auroraに移行した後は、4つのレプリカの最大のレプリカラグが20msを超えることはなかった

f:id:Spring_MT:20210317154610p:plain

Auroraによってレプリカラグが改善されたことで、同社はアプリケーションの負荷の大部分をレプリカに振り向けることができ、コスト削減と可用性の向上を実現できた。

(うーーーーーん 単純にMySQLの設定とかアプリの書き方じゃないのかなあああ、参考にしてはだめな気がする)

参照

www.planetscale.com

ベンチマークの検証はよく考えよう

Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databasesを読む(その5 PUTTING IT ALL TOGETHER)

5 PUTTING IT ALL TOGETHER

ここでは、Figure 5で示すAuroraの構成要素について説明する。

f:id:Spring_MT:20210316035252p:plain

https://www.allthingsdistributed.com/files/p1041-verbitski.pdf 参照

データベースエンジンは "community" MySQL/InnoDBのフォークであり,主にInnoDBがどのようにデータをディスクに読み書きするかで変わる。

"community" InnoDBでは、書き込み操作によってデータがバッファページで変更され、関連するredoグレコードがLSN順にWALのバッファ(メモリ)に書き込まれる。

トランザクションのコミット時には、WALプロトコルは、トランザクションredoグレコードが永続的にディスクに書き込まれることのみが必要になる。

実際に変更されたバッファページは、中途半端なページ書き込みを避けるために、ダブルライトで最終的にディスクに書き込まれる。

これらのページの書き込みは、バックグラウンドや、キャッシュからの退避中、チェックポイントの取得中に行われる。

InnoDBには、IOのサブシステムのほかに、トランザクションサブシステム、ロックマネージャー、B+-Treeの実装、「ミニトランザクション」(MTR)の概念が含まれている。

MTRは、InnoDB内部でのみ使用されるもので、アトミックに実行されなければならない一連のオペレーションを表現したものである(例:B+ ツリーのページ分割/マージとか)。

(詳解MySQL 5.7 p 124付近)

labs.gree.jp

dev.mysql.com

www.slideshare.net

mysqlserverteam.com

Aurora (InnoDBの亜種)において、それぞれのMTRでアトミックに実行されなければならない変更内容を表しているredoグレコードは、各ログレコードが属するPGによってまとめられ、これらのまとまりはストレージサービスに書き込まれる。

MTRの最後のログレコードはconsistency(一貫性)ポイントとしてタグ付けされます。( CPLのことですね )

Auroraのライターでは "community" MySQLがサポートしている分離レベルと全く同等のものをサポートしている。

Auroraのリードレプリカは、ライターのトランザクション開始とコミットに関する情報を継続的に取得し、これらの情報を使ってスナップショット分離をサポートしている。

なお、並行性制御は、ストレージサービスに影響がない形で、全てデータベースエンジンに実装されている。

ストレージサービスは、 "community" InnoDBのローカルストレージへのデータを書き込みしたときと論理的に同一の、根本的には同じデータに対して統一されたビューを見せます。

Auroraは、コントロールプレーンにAmazon Relational Database Service(RDS)を活用している。

RDSはデータベースインスタンス上にホストマネージャ(HM)と呼ばれるエージェントを動かしており、クラスタのヘルスチェックを監視し、フェイルオーバーが必要かどうか、あるいはインスタンスを入れ替える必要があるかどうかを判断する。

各データベースインスタンスは、クラスターの中で1台のライターと0台以上のリードレプリカで構成される。

クラスタインスタンス群は、1つのリージョン(例:us-east-1、us-west-1など)で、通常は異なるAZに配置され、同じリージョンのストレージインスタンス群と接続します。

セキュリティのために、Auroraではデータベース、アプリケーション、ストレージ間の通信を分離している。

実際には、各データベースインスタンスは3つのVPC上で通信することができる。

  • ユーザーのアプリケーションがデータベースエンジンとやりとりするCustomer VPC
  • データベースエンジンとコントロールプレーンが相互に通信するRDS VPC
  • データベースとストレージサービスが相互に通信するStorage VPC

ストレージサービスは、各リージョンの少なくとも3つのAZにまたがってプロビジョニングされたEC2 VMクラスター上にデプロイされ、複数のユーザーのストレージボリュームの提供、ストレージボリュームからのデータの読み書き、ストレージボリュームからのデータのバックアップとリストアを一括して担当する。

ストレージノードはローカルのSSDを操作し、データベースエンジンインスタンス、他の組となるストレージノード、バックアップ/リストアサービス(変更されたデータを継続的にS3にバックアップ、必要に応じてS3からデータをリストア)と相互にやり取りをします。

クラスターとストレージボリュームの設定、ストレージボリュームのメタデータ、S3にバックアップされたデータの詳細な内容の永続化するために、ストレージのコントロールプレーンとしてAmazon DynamoDBを使っている。

aws.amazon.com

Amazon DynamoDB vs. etcd vs. Redis Comparison

データベースボリュームのリストアや、ストレージノードの障害後の復旧など、長時間実行するタスクのオーケストレーションには、Amazon Simple Workflow Serviceを使っている。

dev.classmethod.jp

高いレベルの可用性を維持するためには、エンドユーザーに影響がでる前に、実際の問題や潜在的な問題を積極的に自動化して早期に検知することが必要になる。

ストレージ運用におけるクリティカルな部分は、メトリクス収集サービスを使い常に監視がされており、重要なパフォーマンスや可用性のメトリクスに不安材料があればアラームを起こす。

Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databasesを読む(その4 THE LOG MARCHES FORWARD)

4 THE LOG MARCHES FORWARD

ここでは、永続状態、実行状態、レプリカ状態で常に一貫しているように、データベースエンジンからどのようにログが生成されるかを説明する。(3つの状態があるってことか)

特に、コストが高い2PC(2 phase commit)のプロトコルを使わずに、効率的に一貫性を保つ実装方法を述べる。

(データ指向アプリケーションデザイン p 386あたりを参照する)

まず、クラッシュリカバリー時におけるコストの高いredo処理を回避する方法を示す。

次に、通常の運用時と、実行状態とレプリカ状態の維持の方法を説明する。

最後に. リカバリー処理の詳細を示す。

補足の資料を用意してあります。

spring-mt.hatenablog.com

4.1 解決策の全体像 : 非同期処理

セクション3で説明したように、Auroraではデータベースをredoログストリームとしてモデル化しているので、このredoログが順序だった変更の連続の流れという事実を利用することができる。

実際には、各ログレコードには関連するログシーケンス番号(LSN)があり、これはデータベースによって生成された単調増加する値である。 (MySQL InnoDBのpageの中にあるLSN(Log Sequence Number)は増加し続ける値で、InnoDBredoログに50バイト書き込めば、LSNも50バイト進むようになっています。)

https://dev.mysql.com/doc/refman/5.6/ja/glossary.html#LSN

これにより、やり取りが多く失敗に不寛容な2PCのようなプロトコルの利用なしに、非同期的なアプローチにより状態を維持するための合意プロトコルを単純にできる。

高レベルでは、ある時点での一貫性と耐久性を維持し、未処理のストレージ要求に対するackを受けとりつつ、維持できているポイント継続的に進めていきます。

個々のストレージノードは、1つ以上のログレコードをロストしているかもしれないので、PGの他のメンバーとgossip protocolでやりとりしてギャップを探してロストした分を埋めていく。

状態が失われて再構築しなければならないリカバリ時を除き、データベースによって維持される実行状態( runtime state )においては、クオラムリードではなく一つのセグメントリードを使うことができる。

データベースには複数の未処理の分離されたトランザクションがあり、それらは開始された順序とは異なる順序で完了(完了して耐久性のある状態に到達)する可能性がある。(並行性の問題)

データベースがクラッシュしたり再起動する場合、ロールバックするかどうかの判断は、個々のトランザクションごとに別個に行われる。

(MySQLの例はこれ MySQL :: MySQL 8.0 Reference Manual :: 15.18.2 InnoDB Recovery)

中途半端なトランザクションを追跡し元に戻すロジックは、単純なディスクへの書き込み(つまりcommitのロジック?)と同様に、データベースエンジンに実装されている。

しかし、再起動時において、Auroraのデータベースは、ストレージボリュームへのアクセスが発生するより前に、ユーザーレベルのトランザクションを見ない、ストレージサービスで独自のリカバリを行う。

このストレージサービス独自のリカバリは、分散された性質にもかかわらず、データベースにストレージのビューが全て同じように見えるようにする。

ストレージサービスは、すべてのログレコードの可用性を保証できる一番大きいLSNを決める(これはVCLまたはVolume Complete LSNと呼ばれる)。

ストレージのリカバリ中に、VCLより大きいLSNを持つすべてのログレコードは削除しなければならない。

しかし、データベースは、ログレコードにタグを付け、CPL(Consistency Point LSN)として識別することで、削除が可能なポイントのサブセットをさらに限定できる。

そこで、VDL(Volume Durable LSN)をVCLより小さいが最も大きいCPLと定義し、VDLより大きいLSNを持つ全てのログレコードを削除する。

たとえば、LSN 1007までの完全な( Completeな )データがあるとして、データベースは900、1000、1100がCPLであると宣言しているとしたら、その場合は1000以上で切り捨てる必要がある。

1007までは完全( Complete )で、1000までは耐久性(Durable)がある。

完全性と耐久性は異なるものであり、CPLは、順番に受け入れなければならないストレージシステムのトランザクションのいくつかのまとまりについて線引きするものと考えることができる。

クライアントがこのような区別を必要としない場合は、単純にすべてのログレコードをCPLとしてマークすることができる。

ただ、実際には、データベースとストレージは以下のように相互作用する。

  1. 各データベースレベルのトランザクションは、順序付けられた複数のミニトランザクション(MTR)に分割され、 アトミックに実行される必要がある
    • MySQL InnoDBにおいて、MTRredoログに全て書き込まれている。 再実行することで同じ操作を再現できる。(詳解MySQL 5.7 p 125)
  2. MTRは、連続する複数のログレコード(必要な数だけ)で構成される
  3. MTRの最終ログレコードは CPL である。

リカバリ時には、データベースはストレージサービスとやりとりして各PGの耐久性(PGCL)を持っているポイントを集め、それを利用してVDLを作り、VDL以上のログレコードを切り詰めるコマンドを発行する。

4.2 通常の動作

ここでは、データベースエンジンの「通常の動作」について、書き込み、読み込み、コミット、レプリカに焦点を当てて説明する。

4.2.1 書き込み

書き込みの流れの概要図をはっておく。

f:id:Spring_MT:20210308232405p:plain

Auroraでは、データベースはストレージサービスと継続的にやり取りし、クオラムの確立、ボリュームの耐久性の向上、コミットされたトランザクションの登録ができる状態を維持する。

例えば、通常時(ログが前進する)のフローだと、データベースがログレコード群の書き込みクオラムの成立のackを受け取ると、現在のVDLを進める。

任意の時点で、データベースではトランザクションが並行して発生しており、それぞれが独自のredoグレコードを生成している。

データベースは、現在のVDLとLSN Allocation Limit(LAL)と呼ばれる定数(この時点では1000万)の合計よりも大きい値を持つLSNは存在しないという制約で、各ログレコードに一意で順序だったLSNを割り当てる。

この制限は、ストレージシステムにないログがデータベースシステム上に増えすぎないようにし、ストレージやネットワークが追いつけない場合には書き込みを制限にすることができるバックプレッシャーが かかる。

バックプレッシャーとは - IT用語辞典 e-Words

各PGの各セグメントは、そのセグメントに存在するページに影響を与えるボリュームの中のログレコードの一部部分のみを見ている。

各ログレコードには、そのPG内の以前のログレコードを識別するための被リンクが含まれている

これらのバックリンクは、各セグメントに到達したログレコードの完全性のポイント(PGが受信した全てのログレコードのうち各セグメントの最大のLSN(Segment Complete LSN (SCL)))を追跡するために使用される。

SCLは、各ストレージノードが 不足しているログレコードを見つけ、交換するためのgossip protocolでのやりとりに利用される。

4.2.2 コミット

Auroraでは、トランザクションのコミットは非同期に完了する。

クライアントがトランザクションをコミットすると、コミットリクエストを処理するスレッドは、トランザクションを コミットを待つ別のトランザクションのリストの一部として "コミットLSN" を記録することで処理を完了させて、他の作業を実行する。

最新のVDLがトランザクションのコミットLSN以上である場合に限りコミットを完了することがWALプロトコルと同等のものとなる。

(データ指向アプリケーションデザイン p 170)

VDLが進むと、データベースはコミットを待っている正しいトランザクションを特定し、専用のスレッドを利用して待機中のクライアントにコミットのackを送信する。

ワーカースレッドはコミットのために一時停止せず、単に保留中の他のリクエストをとってきて処理を継続する。

4.2.3 読み取り

Auroraでは、多くのデータベースと同様に、ページはバッファキャッシュから提供され、当該ページがキャッシュに存在しない場合にのみストレージIOリクエストが発生する。

バッファキャッシュがいっぱいになると、システムはキャッシュから退避させるためのページを見さがす。

従来のシステムでは、犠牲となるページが「ダーティページ」であった場合、そのページを後から取得しても常に最新のデータが得られるようにするため、置き換わる前にディスクにフラッシュさる。(MySQLの場合は 詳解MySQL 5.7 p 127参照)

Auroraデータベースは、退避時やそれ以外であってもにページを書き出すことしないが、バッファキャッシュ内のページは常に最新バージョンになることを強く保証している。

これは、ページに対する最新の変更に関連するログレコードを識別するためのpage LSNがVDL以上の場合においてのみ(only ifって書いてある)キャッシュからページを退避するという実装によって実現されている。

(この実装だと、古いページキャッシュが残り続けてキャッシュを圧迫しそうなのでLRUもあると思うが、Auroraにおけるページキャッシュのevictionに関しては、記述はこの論文しか見当たらず、これだけのevictionかどうかわからない。。。。)

このプロトコルは以下のことを保証している。

  1. ページ内のすべての変更がログに固まっている
  2. キャッシュミスの際、最新の耐久性のあるバージョンを取得するためには、現在のVDLの時点でのページのバージョンを要求すれば十分であること

通常の状態では、データベースは読み取りクオラムを使用してコンセンサスを確立する必要はない。

ディスクからページを読み取る場合、データベースはリクエストが発行された時点のVDLを表す読み取りポイント( read-point )を確立する。

その後、データベースは読み取りポイントに対して完全(complete)であるストレージノードを選択し、その結果、最新のバージョンを受け取ることができる。

ストレージノードから返されるページは、データベース内のミニトランザクションMTR)の期待される形式と一致していなければならない。

データベースは、ストレージノードへのログレコードの提供と、進捗状況の追跡を直接管理している(各セグメントのSCLなどのこと)ので、通常ならば読み取りを満たすことができるセグメント(SCLが読み取りポイントより大きいセグメント)がわかり、十分なデータを持つセグメントに直接読み取り要求を発行することができる。

データベースは未処理の読み取りをすべて監視しているので、PGごとに最小の読み取りポイントLSNをいつでも計算することができます。

リードレプリカがある場合、ライターはそのレプリカとgossip protocolでやり取りして、すべてのノードでPGごとの最小の読み取りポイントLSNを作る。

この値はPGMRPL(Protection Group Min Read Point LSN)と呼ばれ、あるPGのすべてのログレコードが不要となる"低水位"(底)を表す。

言い換えれば、ストレージノードセグメントは、PGMRPLよりも低い読み取りポイントを持つ読み取りページ要求が存在しないことが保証される。

各ストレージノードはデータベースからPGMRPLを認識しているため、古いログレコードを合体させ、安全にGCすることで、ディスク上の実体化されているページを前に進めることができます。

また実際の並列制御のプロトコルは、従来のMySQLのように、ページとUndoセグメントがローカルストレージに構築されているのとまったく同じようにAuroraのデータベースエンジンで実行される。

4.2.4 レプリカ

Auroraでは、1台のライターと最大15台のリードレプリカが、1つの共有ストレージボリュームをマウントすることができる。

そのため、リードレプリカを追加しても、消費されるストレージやディスクの書き込み操作による追加コストは発生しない。

遅延を最小限に抑えるために、ライターで生成されストレージノードに送信されるログストリーム(redoログのストリームってことかな)は、すべてのリードレプリカにも送信される。

リーダーでは、データベースが各ログレコードの順序を考慮しながらログストリームを消費する。

グレコードがリーダのバッファキャッシュ内のページを参照している場合(更新系の処理ってことかな)は、ログアプリケータを使用してキャッシュ内のページに指定されたredoログの操作を適用する。

それ以外の場合は単にログレコードを破棄する。

レプリカとは関係なくユーザーコミットをackするライターの観点からだと、レプリカはログレコードを非同期に消費することに注意する。

レプリカがログレコードを適用するときには下記の2つの重要なルールに従う。

  1. LSN がVDL以下のログレコードだけが適用される
  2. レプリカがすべてのデータベースオブジェクトの一貫したビューを見ることができるようになるために、1 つのMTRの一部であるログレコードはレプリカのキャッシュの中でアトミックに適用される

(アトミック: データ指向アプリケーション p242あたりを参照)

実際には、通常、各レプリカはライターから短い間隔(20ms以下)で遅れている。

4.3 リカバリ

従来のデータベースの多くは、ARIESなどのリカバリプロトコルを使用しており、コミットされたすべてのトランザクションの正確な内容を表すことができるWAL (Write-ahead Log)の存在に依存している。

qiita.com

nippondanji.blogspot.com

(MySQLではこれはredoログとundoログ)

また、これらのシステムでは、ダーティページをディスクにフラッシュし、ログにチェックポイントレコードを書き込むことで、定期的にデータベースのチェックポイントを作成し、荒い粒度ではあるが耐久性が保証されているポイントを確立する。

www.slideshare.net

The relationship between Innodb Log checkpointing and dirty Buffer pool pages - Percona Database Performance Blog

再起動時には、どのページにもコミットされたデータの欠損、コミットされていないデータが含まれている可能性がある。

そのため、クラッシュリカバリー時には、ログアプリケーターを使って、最後のチェックポイント以降のredoグレコードを処理し、各ログレコードを対象のデータベースページに適用する。

このプロセスにより、データベースページは障害発生時点における一貫性がある状態に戻るので、その後、undoログレコードを実行することで、クラッシュ中で実行中のトランザクションロールバックすることができる。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 14.16.1 InnoDB のリカバリプロセス

(詳解MySQL 5.7 p 121 p 142)

クラッシュリカバリーはコストが高い作業である。

チェックポイント作成の間隔を短くすると効果的ですが、フォアグラウンドのトランザクションと干渉してしまう。

Auroraではそのようなトレードオフは必要ない。

従来のデータベースの一つ単純な原則は、フォワード処理パスでもリカバリーでも同じredoログアプリケータが使われ、データベースがオフラインの間は、同期的にフォアグラウンドで動作する。

Auroraでも方針は同じだが、redoログアプリケーターはデータベースから分離され、ストレージノード上で並列に、常にバックグラウンドで動作する。

そのため、データベースが起動すると、ストレージサービスと連携してボリュームリカバリーを実行するのだが、Auroraデータベースは1秒間に10万件以上の書き込み処理しているときにクラッシュしても、非常に素早く(通常は10秒以下)回復することができる。

クラッシュ後は、データベースはランタイム状態に再構築する必要がある。

この場合、データベースは各PGごとに、書き込みクオラムに到達した可能性のあるデータの検出を保証するのに十分なセグメントの読み取りクオラムを確立する。

(ここで読み取りクオラムが使われる!!!!)

www.slideshare.net

データベースは、すべてのPGに対して読み取りクオラムを確立すると、VDLを再計算(VCLより小さいが最も大きいCPL)し、新しいVDL(Volume Durable LSN)以降のすべてのログレコードを削除する切り捨て範囲を生成する。

(ここでVDLと書いてあるが、別の論文だとVCLってあったりして混乱する、VDLが正しいと思う)

www.slideshare.net

データベースが証明できる最終LSNは、これまでに見られた可能性のある最も先の未処理ログレコードと少なくとも同じ大きさである。

データベースがLSNを割り当てており、VDLを超えて割り当てられるLSNの範囲を制限している(1000万の制限)ため、この上限を推定する。

切り捨て範囲はエポック番号でバージョン管理され、ストレージサービスに永続的に書き込まれる。

これにより、リカバリが中断されたり再開されたりしても、切り捨ての耐久性について問題はなくなる。

クラッシュリカバリー後の新たなredoレコードには、切り捨て範囲以上のLSNが割り当てられる。

( http://pages.cs.wisc.edu/~yxy/cs764-f20/papers/aurora-sigmod-18.pdf 2.4 Crash Recovery in Aurora 参照)

データベースは、クラッシュリカバリーの一環としてredoのリプレイは必要はないが、クラッシュ時に実行中のトランザクションの操作を元に戻すために、undo リカバリーを行う必要がある。

しかし、undoリカバリーは、システムが、undoセグメントから実行中のトランザクションのリストを構築した後、データベースがオンラインになったときにアクティブだったトランザクションの取り消しを行う。

参照

  • page cache周り

www.slideshare.net