6.5K Views
July 05, 25
スライド概要
JAWS ミート 2025
2025/7/5
Qiita や Zenn でいろいろ書いてます。 https://qiita.com/hmatsu47 https://zenn.dev/hmatsu47 MySQL 8.0 の薄い本 : https://github.com/hmatsu47/mysql80_no_usui_hon Aurora MySQL v1 → v3 移行計画 : https://zenn.dev/hmatsu47/books/aurora-mysql3-plan-book https://speakerdeck.com/hmatsu47
ゲームで体感! Aurora DSQL の OCC (楽観的同時実行制御) JAWS ミート 2025 2025/7/5 まつひさ(hmatsu47)
自己紹介 松久裕保(@hmatsu47) ● https://qiita.com/hmatsu47 ● 現在: ○ 名古屋で Web インフラのお守り係をしています ○ SRE チームに所属しつつ技術検証の支援をしています ○ 普段カンファレンス・勉強会では DB の話しかしていません (ほぼ) 2
本日の内容 ● Aurora DSQL おさらい ○ 今年の 5/27 に GA ■ 東京・大阪でシングルリージョン構成をリリース ● OCC(楽観的同時実行制御)おさらい ○ 通常の RDBMS(PCC)との違い ● ゲームで確かめよう! ● 答え合わせ 3
Aurora DSQL おさらい 4
サーバーレス分散 SQL データベース ● PostgreSQL ワイヤープロトコル互換 ○ psql コマンドが使える ● シングルリージョン構成とマルチリージョン構成がある ○ マルチリージョン構成は US 3 リージョン/欧州 3 リージョン/ 東京+大阪+ソウルの組み合わせでサポート ■ エンドポイントは 2 リージョン、残り 1 つは Witness リージョンで構成 5
国内のみでつくるならシングルリージョン構成 引用元 : https://aws.amazon.com/jp/blogs/news/introducing-amazon-aurora-dsql/ 6
それぞれの階層で負荷等に合わせて水平スケール AWS Summit Japan 2025 AWS-43 資料より 引用元 : https://aws.amazon.com/jp/blogs/news/introducing-amazon-aurora-dsql/ 7
OCC おさらい 8
シャーディングを使わずにスケールする…? ● 楽観的同時実行制御(OCC)を採用 ○ 一般の RDBMS は悲観的同時実行制御(PCC)を採用 ■ ロック機構を使う ○ OCC ではロックを使わない ■ コミット時に他のトランザクションとの更新競合を検知したらアボート ■ アボート後必要に応じてリトライ処理(アプリケーション側で実装) ○ ロックしないので他のトランザクションを待たせることがない ■ ただし更新競合が頻発するとアプリケーションの性能が下がる欠点がある 9
例 [1] 通常の RDBMS(PCC / READ COMMITTED) トランザクション A トランザクション B 開始(BEGIN) テーブル X の id = 1 の行 (コミット済み) 10(初期値) 開始(BEGIN) テーブル X の id = 1 の値を +1 →id = 1 の行ロック獲得成功 (11) (別の処理を実行) テーブル X の id = 1 の値を +1 →id = 1 の行ロック獲得待ち コミット(COMMIT)→成功 (↑行ロック獲得待ち) 11 id = 1 の行ロック獲得成功 (12) (別の処理を実行) コミット(COMMIT)→成功 12 10
例 [2] Aurora DSQL(OCC / SNAPSHOT ISOLATION) トランザクション A トランザクション B テーブル X の id = 1 の行 (コミット済み) 開始(BEGIN) 10(初期値) 開始(BEGIN) テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 (別の処理を実行) テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 コミット(COMMIT)→成功 (別の処理を実行) 11 コミット(COMMIT) →失敗・アボート 必要ならリトライする 11
さて問題です トランザクション A トランザクション B テーブル X の id = 1 の行 (コミット済み) 開始(BEGIN) 10(初期値) 開始(BEGIN) テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 (別の処理を実行) どちらかの タイミングで 11 に テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 コミット(COMMIT)→?? コミット(COMMIT)→?? ?? ?? 成功するのはどっち? 12
ゲームで 確かめよう! 13
「最後にコミットした人が勝ち!」ゲーム ● https://dsql.hmatsu47.nagoya/ または ● https://bit.ly/4nxEYrw ● 1. 最初に名前を登録 14
「最後にコミットした人が勝ち!」ゲーム ● https://dsql.hmatsu47.nagoya/ または ● https://bit.ly/4nxEYrw ● 2. ゲームが始まったら制限時間内に攻撃ボタンを押す ○ 押すと DSQL 上のテーブル行を UPDATE → 1 秒待つ→ COMMIT ○ 同時に複数の人が攻撃した場合、COMMIT が成功した人が勝ち ■ 勝つと UPDATE → COMMIT の待ち時間が 1 秒増える(最大 5 秒まで) ■ 負けると 1 秒にリセット 15
「最後にコミットした人が勝ち!」ゲーム ● https://dsql.hmatsu47.nagoya/ または ● https://bit.ly/4nxEYrw ● 3. 攻撃ボタンは時間内に何度押しても OK ● 4. 制限時間内で一番最後に COMMIT した人が優勝! ○ ボタンを押した後の COMMIT が制限時間外なら攻撃失敗 16
答え合わせ 17
もうお分かりですね? トランザクション A トランザクション B テーブル X の id = 1 の行 (コミット済み) 開始(BEGIN) 10(初期値) 開始(BEGIN) テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 (別の処理を実行) どちらかの タイミングで 11 に テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 コミット(COMMIT)→?? コミット(COMMIT)→?? ?? ?? 成功するのはどっち? 18
答え トランザクション A トランザクション B 開始(BEGIN) テーブル X の id = 1 の行 (コミット済み) 10(初期値) こちらの タイミングで 11 に 開始(BEGIN) テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 (別の処理を実行) テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 コミット(COMMIT)→成功 11 コミット(COMMIT)→失敗 成功するのは B!(コミットまでの所要時間が長いトランザクションが不利) 19
答え トランザクション A トランザクション B 開始(BEGIN) 10(初期値) 開始(BEGIN) テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 (別の処理を実行) テーブル X の id = 1 の行 (コミット済み) こちらの タイミングで 11 に 最初の更新から コミットまでの 所要時間の長さ テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 コミット(COMMIT)→成功 11 コミット(COMMIT)→失敗 成功するのは B!(コミットまでの所要時間が長いトランザクションが不利) 20
ロックしないので トランザクション A トランザクション B 開始(BEGIN) テーブル X の id = 1 の行 (コミット済み) 10(初期値) 開始(BEGIN) テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 (別の処理を実行) テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 コミット(COMMIT)→成功 11 コミット(COMMIT)→失敗 「BEGIN / UPDATE が A のほうが先だから」という理由で A を優先するのであれば、 B はここで失敗することになる(A のコミットを待たずに B を失敗させるしかない) 21
ところが トランザクション A トランザクション B 開始(BEGIN) テーブル X の id = 1 の行 (コミット済み) 10(初期値) 開始(BEGIN) テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 (別の処理を実行) テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 コミット(COMMIT)→成功 11 コミット(COMMIT)→失敗 A のコミットを待たないのに B が失敗するのであれば、後でコミットした A が 何らかの理由で失敗した場合、結果的に A も B も失敗することになる→ TPS 低下 22
一方で、トランザクションの一貫性を保つには トランザクション A トランザクション B 開始(BEGIN) テーブル X の id = 1 の行 (コミット済み) 10(初期値) 開始(BEGIN) テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 (別の処理を実行) テーブル X の id = 1 の値を +1 →id = 1 の行 : 11 コミット(COMMIT)→成功 11 コミット(COMMIT)→失敗 B も A も成功なら最終的に値が 12 になる必要があるがそうならない 23
つまり ● 先に COMMIT した者勝ち! ○ 先に BEGIN しても INSERT / UPDATE しても関係なし ○ 結果として無用な待ち時間が発生せず、一貫性も保たれる 24
Aurora DSQL は 特性を理解して 正しく使いましょう! 25