はじめに、の前に、、、
元々Aurora DSQLについての説明を求められた際に色々と端折って話していたのですが、同時実行制御の部分は自身の理解も薄かったため、改めて説明する体で勉強した内容をまとめています。記載としてわかり辛い箇所や理解が薄い箇所、特にトランザクションの部分を図示した方が良いかも、みたいなところがあるかもしれませんので、コメントもしくは直接Twitterなどで連携頂ければ幸いです。
はじめに
Aurora DSQLはAWS re:invent 2024のKeynoteで発表された、AWSが提供する低レイテンシ・マルチリージョンで強整合性を持つ分散(Distributed)なRDBMSになります。
他にも次のAWS blogやre:inventのセッション、開発者のMarcのblogでアーキテクチャーなどが紹介/説明されていますので、興味ある方は覗いてみてください。
- Amazon Aurora DSQL の紹介
- AWS re:Invent 2024 - Get started with Amazon Aurora DSQL (DAT424)
- AWS re:Invent 2024 - Deep dive into Amazon Aurora DSQL and its architecture (DAT427-NEW)
- DSQL Vignette: Reads and Compute
ここで、最初のDSQL紹介blogで次のような記述がありました。
従来のデータベースとは異なり、Aurora DSQL は、従来のロック方式ではなく、楽観的同時性制御 (OCC) を使用しています。スケールアウトするにつれ、OCC により、長時間のトランザクションが他の実行中のトランザクションを遅らせることはありません。Aurora DSQL における OCC の詳細については、Amazon Aurora DSQL の同時性制御 を参照してください。
このリンク先(Amazon Aurora DSQL の同時性制御)では、Aurora DSQLは一般的なRDBMSで採用されている悲観的同時性制御(以下、PCC:Pessimistic Concurrency Control)ではなく、楽観的同時性制御 (以下、OCC:Optimistic Concurrency Control)を用いて同時実行性の制御を行っている、と説明されています。
DSQLにおける同時実行制御についての説明を確認していきます。
本記事はAurora DSQLの同時実行制御について他の一般的なRDBMSとの違いについての話になります。DSQLのアーキテクチャーや分散合意アルゴリズムの話などについては触れませんのでご承知おきください
DSQLにおける同時実行制御
まず、Aurora DSQLにおいては楽観的同時実行制御(OCC)が用いられている、と表現されています。そして、Amazon Aurora DSQL の同時性制御 や コンカレンシーの種類 などではOCCとPCCについて次のように説明されています。
-
OCC(楽観的同時実行制御)
- リソースのロックを行わずにトランザクションを進行
- コミット段階でのみ競合をチェック
- 低競合環境で最も優れたパフォーマンスを発揮
-
PCC(悲観的同時実行制御)
- ロックを利用して、あるトランザクションがその他のトランザクションに影響を与えるような形でデータが変更されることを防ぐ
- トランザクションがロックを適用するアクションを実行すると、オーナーがロックを解放するまで、その他のトランザクションはロックと競合するアクションを実行できない
そのほかにも、次のような制御方法があると言われています1。
- 時刻印順序制御(TO: Timestamp Ordering)
- 多版同時実行制御(MVCC: Multi Version Concurrency Control)
通常よく利用されているRDBMS(Oracle Database2、PostgreSQL3、MySQL4)においては、読み取りはMVCC、書き込みはPCCで同時実行制御が提供されているような形になります。
トランザクション分離レベル
同時実行制御の説明文中でトランザクションが出てきたので、改めてトランザクションについて簡単に振り返っておきます。トランザクションをそのまま直訳すると「取引」となります。ある取引において一連の流れの単位、よく例えられるのは銀行の取引における送金処理のように、幾つかの処理が「ひとまとまり」となって意味を持つような単位になります。
データベースにおいては「データベースに対するアプリケーションプログラムレベルでのひとつの原子的な作用5」とされています。
「トランザクション」が「ひとつの原子的な作用」として動作するためには次の性質を持っている必要があります。基本情報処理試験などでもよく問われるACID特性になります。
- ACID
- Atomicity(原子性)
- Consitency(一貫性、整合性)
- Isolation(独立性、隔離性)
- Durability(耐久性)
トランザクション分離レベルは、ACIDにおけるIsolationについての話になります。
Isolationとしては「複数のトランザクションを並列で実行した場合にも他のトランザクションの影響を受けない」ような状況を指します。
情報処理試験などでもお馴染みのとおり、ANSIでは次表の4つのトランザクション分離レベル(Isolation level)が定義されており、それぞれのレベルで発生しうる異常(Anomaly: Dirty Readなど)が規定されています。
それぞれの異常については解説書やWebでも確認できると思いますので、改めて確認したい方はそちらを参照ください。
トランザクション分離レベル | Dirty Read | Fuzzy Read | Phantom |
---|---|---|---|
READ UNCOMMITTED | ○ | ○ | ○ |
READ COMMITTED | - | ○ | ○ |
REPEATABLE READ | - | - | ○ |
SERIALIZABLE | - | - | - |
- (横軸は異常(Anomaly)、異常が ○:発生しうる、‐:発生しない)
ここで、ANSIのトランザクション分離レベルはそれぞれの異常(Dirty Readなど)が発生しない状態のみがそれぞれの分離レベルとして規定されており、具体的な実装については定義されておりません。そのため、データベースエンジン毎(例えばOracle Database と PostgreSQLなど)で違う動作になる場合があります。
そして、この4つの分離レベルだけでは適切に説明できない、として1995年にマイクロソフト等から次表のような分離レベルが提言されました6(こちらも異常の内容については引用先を参照ください)。
(詳細な)トランザクション分離レベル | Dirty Write | Dirty Read | Cursor Lost Update | Lost Update | Fuzzy Read | Phantom | Read Skew | Write Skew |
---|---|---|---|---|---|---|---|---|
READ UNCOMMITTED | - | ○ | ○ | ○ | ○ | ○ | ○ | ○ |
READ COMMITTED | - | - | ○ | ○ | ○ | ○ | ○ | ○ |
Cursor Stability | - | - | - | △ | △ | ○ | ○ | △ |
REPEATABLE READ | - | - | - | - | - | ○ | - | - |
Snapshot Isolation | - | - | - | - | - | △ | - | ○ |
SERIALIZABLE | - | - | - | - | - | - | - | - |
- (○:発生しうる、△:状況により発生しうる、‐:発生しない)
また、さらに細かいレベルでも議論が行われています7。
日本語の書籍だとオライリーの データ指向アプリケーションデザイン で上図も含めて解説されていますので、興味がある方は確認してみてください。
Aurora DSQLのトランザクション分離レベル
Aurora DSQLのトランザクション分離レベルは以下の通り、PostgreSQLのリピータブルリード(repeatable read)分離レベルが提供されています。
Aurora DSQL は PostgreSQL の repeatable read 分離レベルに相当する強力なスナップショット分離をサポートしています。トランザクションは、開始時のデータベースのスナップショットからデータを読み取ります。
(引用:Amazon Aurora DSQL の同時性制御)
ここで「PostgreSQL の repeatable read 分離レベルに相当する強力なスナップショット分離」とは、回りくどい言い方ですが次の通り スナップショット分離レベル(Snapshot Isolation) を指します(PostgreSQLのリピータブルリードレベルがスナップショット分離レベルに相当するため)。
リピータブルリード分離レベルは、学術的なデータベースの文献や他のデータベース製品のいくつかではスナップショット分離として知られる技術を用いて実装されています。 同時実行性の面で劣る伝統的なロック技術を使うシステムと比較すると振舞いや性能の違いが観察されるかもしれません。 他のシステムでは、リピータブルリードとスナップショット分離を異なる振舞いをする別の分離レベルとして提供しているかもしれません。 2つの技術を区別する許容される現象は、標準SQLが制定されるまではデータベース研究者により定式化されておらず、この文書の範囲を超えます。
(引用:リピータブルリード分離レベル)
改めて スナップショット分離 レベルについて説明すると、次のようになります。
- トランザクションが開始される前までにコミットされたデータのみを参照
- コミットされていないデータや、そのトランザクションの実行中に別のトランザクションでコミットされた変更を参照しない
- その問い合わせと同じトランザクション内で行われた過去の更新は参照する
通常のデータベースで標準として利用されていることが多いリードコミッティド(READ COMMITTED)ではトランザクション内でなく、そのSQLを開始するときにコミットされたデータのみを参照する、という形になります。一方このトランザクション分離(Snapshot Isolation)ではそのトランザクションが開始される前にコミットされたデータのみを参照する、という点が違う点になります。
少し話は逸れますが、リードコミッティド(READ COMMITTED)を利用する場合にはアプリケーション内で SELECT FOR UPDATEなどを利用してロックをかけつつ一貫性を確保する、というような実装になることも多いと思います。
Aurora DSQLの同時実行制御
前述の状況を踏まえ、Aurora DSQLの同時実行制御に話を戻します。
DSQLの開発者である Mark Brookerによると、次のように実装されていると説明されています。
- 読み取り:MVCC(多版同時実行制御)+TO(時刻印順序制御)
- 書き込み:OCC(楽観的同時実行制御)
読み取りでは Re:invent の基調講演で Matt が話していた Amazon Time Sync Service による厳密な時刻印によって、厳密にバージョン管理(MVCC)を行うことが可能となります。
一方で書き込みはOCCでロックを取らずコミットのタイミングでのみ競合がチェックされる形になり、競合が発生しない環境では低レイテンシでの書き込みを実行することが可能となっています(トランザクションの順序制御などにTOも利用されていると考えてます)。
まとめ
Aurora DSQLにおいては、同時実行制御の実装のされ方が既存で利用しているRDBMSとは違ってくるため、既存のデータベースのアップグレードや置き換えという形ではなく、新たにスケーラブルなアプリケーションを作りたい場合の選択肢の一つとして利用していく形が望ましいと考えられます。
開発者のMarcも ジッターを伴うタイムアウト、再試行、およびバックオフ においてリトライロジックについて補足しています。
現時点(2025/2/2)においてはコストについては未発表ですが、DSQLの利用形態を想定するとスモールスタートでも利用してほしいと考えられていると想定されますので、そういったニーズにも対応できるようなコスト形態になると思います。
その前に現在のプレビューでは無償で利用できるので、興味ある方は実際のアプリで動きを確認されてみると良いと考えてます。
おまけ(Cloud Spannerの同時実行制御)
DSQLと分散RDBMSとして比較されることも多い Google Cloudの Cloud Spannerでは、どちらかというと 強整合性 を強く意識したつくりになっており、あるトランザクションの読み取りに対して別のトランザクションの書き込みが待機するような状況も発生しえます(読み取り専用トランザクションを利用することなどで回避することは可能)。
これは、Cloud Spanner自体はレイテンシの低減や書き込み競合の回避という形よりも、書き込みのQPSを高めつつ従来の利用の仕方を継続させたい、という背景があると考えてます。
Spannerの同時実行制御について知りたい方は、次のリンクが参考になると思います。歴史があるので検証含めていろいろと説明されており、改めてDSQLとの思想の違いなども確認できると考えてます。
- Spanner: TrueTime と外部整合性
- Cloud Spanner を使って様々な Anomaly に立ち向かう
- Cloud Spanner におけるトランザクションの排他制御
- Cloud Spanner のロックについて