本記事ではどのトランザクション分離レベルにすることで、どんな現象を防ぐことができるかについて。何度も見聞きするがよく忘れてしまうので、図説して絵で確認していきます。
トランザクションとは?
トランザクションの基本的要点は複数の手順を単一の「全てかなしか」の操作にまとめ上げることです。 (参考:PostgreSQL 11.5文書)
簡単に言うと
- 1つの作業単位として扱う一連の操作の集まりです
- トランザクション内の操作は全て実行されるか、または全て実行されないです
トランザクションが活躍する場面でよく使われる例は、銀行の振り込みです。
例えばAさんがBさんに1万円を振り込むとき、
- Aさんの口座から出金されたが、Bさんへの口座への入金に失敗
- Bさんへの口座への入金に成功しているが、Aさんの口座から出金されていない
といった問題を防ぐため、出金と入金の処理をまとめてトランザクションにします。
図を見ると、処理は2つありますがそれらをまとめて**トランザクション**としています。 処理結果は、**どちらも成功の場合のみ**トランザクションの処理を全て実行するようにしていますね。これで、入出金の失敗が防げるようになりました。
トランザクションによって起こりうる現象
こんな便利なトランザクションですが、使うことで起こる可能性のあるよく知られた現象が3つあります。
1. ダーティリード
2. ノンリピータブルリード(ファジーリード)
3. ファントムリード
ダーティリード
別のトランザクション(絵ではトランザクションB)でまだコミットされていないデータを、他のトランザクション(絵ではトランザクションA)内で読み込んでしまう現象
まだ処理が終わっていないデータを読み込んでしまっています。
トランザクションAが失敗した場合
**「Aさんの口座から1万円をマイナス」されないこと(ロールバック)**になりますが、
Cさんは「Aさんの口座から1万円をマイナス」された結果を見てしまいます。
ノンリピータブルリード(ファジーリード)
あるトランザクション(絵ではトランザクションB)でデータを複数回読み取っている途中で、
他のトランザクション(絵ではトランザクションA)でデータを**書き換え(更新し)**てしまい、
途中から違う結果のデータを読み取ってしまう現象
ファントムリード
あるトランザクション(絵ではトランザクションB)の途中で、
他のトランザクション(絵ではトランザクションA)によってデータが挿入/削除されてしまい、
途中から読み込むデータ(の数)が異なってしまう現象
トランザクション分離レベル
トランザクションによって起こってしまう3つの問題を見てきました。
いずれもトランザクション中に他の処理が入ってしまうことで起きる問題です。
これらの問題を防ぐために、トランザクションはトランザクション中の処理に対して他の処理がどの程度介入できるかを設定することができます。
これをトランザクション分離レベルと呼びます。トランザクション分離レベルは4つあります。
- Read Uncommited
- Read Commited(PostgreSQLのデフォルト)
- Repeatable Read(MySQLのデフォルト)
- Serializable
下に行くほど強い分離レベルと呼ばれ、「ダーティリード、ノンリピータブルリード、ファントムリード」の問題を防ぐことが可能になります。
※Serializableが必ず良いと言うわけではありません