こんにちは、4年目エンジニアの nakaSay です。
本記事では「データベースのきほん」という書籍の内容をかい摘み、いくつかのパートに分けて記事を投稿しています。
本書籍は入門書であり、記事では以下のような内容を取り上げる予定です。気になった方は是非気軽にお立ち寄りください。
- データベースの全体像
- リレーショナルデータベースとは
- データベースに関わるお金の話
- データベースとアーキテクチャ構成
- DBMS を操作するときの基本知識
- SQL文の基本
- トランザクションと同時実行制御
- テーブル設計の基礎
- バックアップとリカバリ
トランザクションとは
ここでは、一般的なDBMSでアプリケーションのロジックを構成する場合に利用するトランザクションやロックの仕組みを説明します。
前章では基本SQLについて説明しましたが、アプリケーションでは複数のクエリを連続的に実行することがほとんどです。
この一連の複数クエリをひとまとまりにしたものをトランザクションと呼びます。
ACID特性
トランザクションは次の4つの特性により定義され、そのイニシャルによって「ACID特性」と呼ばれます。
- Atomicity(原子性)
- Consistency(一貫性)
- Isolation(分離性)
- Durability(持続性)
Atomicity(原子性)
一連のデータ処理が「全部成功」か「全部失敗」のどちらかの状態になることを保証すること。
トランザクションでは、全ての処理が成功した後に「COMMIT」を発行して処理を確定させます。
もし途中で問題が発生した場合は「ROLLBACK」を発行し、処理開始直前の状態に戻す仕組みです。
Consistency(一貫性)
トランザクションの前後で、データベースが持つ制約などを満たした状態の維持を保証すること。
Isolation(分離性)
一連のデータ操作が、複数ユーザーから同時に行われる際に、「それぞれの処理が順次実行された結果と同じ結果が得られる」状態を保証すること。
データベースでは、同じデータを同時に更新することを防ぐために「ロック」をかけて、後続の処理をブロックする仕組みがあります。
ロックの単位には「表全体」、「ブロック単位」、「行単位」があり、MySQLでは主に行単位でのロックを利用します。
後続の処理はロックが解放される(COMMITまたはROLLBACK)まで待たされ、正しく処理が継続できるという仕組みです。
しかし、全て完璧に直列性を維持するとパフォーマンス的に実用に耐えません。
なので、「ANSI」という規格団体によって以下の分離レベルが定義されています。
- 非コミット読み取り(Read Uncommitted)
- コミット済み読み取り(Read Committed)
- 再読み込み可能読み取り(Repeatable Read)
3. 同じクエリを複数回実行すると、初回読込の結果セットが返され続ける - 直列化可能(Serializable)
1~4にかけて分離レベルが厳しくなっていきます。
また、分離レベルが緩くなることに以下の現象が発生するようになります。
- ダーティリード(Dirty Read)
- コミット確定前(汚れた)のデータを参照してしまう現象
- 曖昧な読取(Fuzzy Read)
- トランザクションで2回目以降に読み込んだデータが1回目に読み込んだ結果と異なる現象
- ファントム(Phantom)
- トランザクションで、データが現れたり消えたりする現象
分離レベル | ダーティリード | 曖昧な読取 | ファントム |
---|---|---|---|
リードアンコミッテッド | ⚪︎ | ⚪︎ | ⚪︎ |
リードコミッテッド | - | ⚪︎ | ⚪︎ |
リピータブルリード | - | - | ⚪︎ |
シリアライザブル | - | - | - |
MySQL では、デフォルトの分離レベルはリピータブルリードとなっています。
Durability(持続性)
一連のデータ操作が完了し、完了通知をユーザーが受け取った時点でその操作が永続的となり、結果が失われない状態を保証すること。
MySQLの特性
MhySQL(InnoDB型テーブル)は、現在DBMSの主流となっている「MVCC(Multi Versioning Concurrency Control)」という技術を用いている。
MVCCの利用により、MySQLは以下のような特性があります。
- 「更新」と「読込」はお互いにブロックしない(「読込」と「読込」もお互いにブロックしない)
- 「更新」の際は行ロックを取得し、トランザクションが終了するまで保持する
- 「更新」と「更新」は、後続のトランザクションがロックを取得しようとしてブロックされる。一定時間待ち、その間にロックが取得できない場合はロックタイムアウトになる
- 「更新」した場合、更新前のデータをUNDOログとして「ロールバックセグメント」という領域に持つ。同じ行を更新するごとにUNDOログは作成され、同じ行に対して複数のバージョンが存在する。
a. ロールバック時に更新前の状態に戻すことが可能となる
b. 複数トランザクションから分離レベルに応じて、対応する更新データを参照するために利用する
ロックタイムアウトとデッドロック
ロックタイムアウト
「更新」と「更新」がぶつかった際に、後続の更新がロック待ちの状態となり、それがタイムアウトとなること
デッドロック
2つのトランザクションがお互いのロック済みリソースを更新しようとしてしまい、いくら待っても状況が変わらない状態
まとめ
- DBMSのトランザクションは「ACID特性」を持つ
- トランザクションは「Atomicity(原子性)」により全部成功するか、全部失敗するかの原則で動作する
- 「Isolation(分離性)」により、並列実行が矛盾なく行える
- トランザクションには4つの分離レベルがあり、実運用では「リードコミッテッド」もしくは「リピータブルリード」の分離レベルを利用する
- トランザクションではロック待ちとデッドロックは避けられないため、適切に対処することが必要
参考