トランザクションとは
今回のテーマである「トランザクション」とは一体何のことでしょうか?「オススメ!OSS-DB情報」の第1回でも取り上げられているのでご存じの方も多いことでしょう。(記憶の曖昧な方はぜひ読み返してみてください)
そちらの記事にも出ているように、トランザクションとは複数のSQL文を一連のものとして扱う処理単位を意味します。
そしてこのトランザクションの重要な点は、一連のSQLが「すべて完全に実行される」か「まったく実行されない」かのどちらかしかなく、一部分だけが実行されるような中途半端な状態は許されないことにあります。
複数のSQL実行を処理単位とするようなケースは、実際の業務フローの中でもしばしば登場します。
例えば、AさんからBさんへ10万円を送金する場合を考えてみましょう。処理は次のような流れで行われるものとします。
Aさんの口座から10万円を引く
Bさんの口座へ10万円を加える
1.が実行された直後に何らかの理由で処理が中断されたとしたらどうなるでしょうか。10万円がまるまる消失してしまうことになりますね。このような事態を避けるためにも、1.と2.はセットで扱わなければいけないことが理解できるかと思います。
PostgreSQLでは、トランザクションを定義するコマンドとしてBEGIN/COMMITを使用します。BEGINコマンドを実行すると、トランザクションが開始したとみなされ、それ以降に実行されるSQLは、COMMITが実行されるまでが一連のトランザクションとして扱われます。仮に途中でエラーが起きたとしても、BEGIN以降の処理はすべて無効化されますので、データの不整合を回避することができます。
※エラー発生時以外にも、ROLLBACKコマンドを実行することで明示的にトランザクションを無効化することも可能です。
先述の例で言えば、次のようにすることでひとつのトランザクションとして実行できるようになります。
BEGIN;
Aさんの口座から10万円を引く
Bさんの口座へ10万円を加える
COMMIT;
トランザクション分離レベル
PostgreSQLはマルチプロセスで処理が行われますので、当然ながら複数のトランザクションが同時に実行されることになります。トランザクション同士が相互に影響することにより、次のような不整合を生じる可能性があります。
ダーティリード
他のトランザクションが更新した未コミット状態のデータを読み込んでしまうこと。
反復不能読み取り
トランザクション内で同じレコードを2回読み込んだとき、読み込み間隔内に他のトランザクションが該当レコードの更新をコミットしたことで、1回目と2回目の読み込み結果が変わってしまうこと。
ファントムリード
トランザクション内で同一条件のレコード抽出を2回行ったとき、読み込み間隔内に他のトランザクションがレコードの挿入/削除をコミットしたことで、1回目と2回目の結果のレコード数が変わってしまうこと。
このようなトランザクション同士の干渉を防ぐためには、個々のトランザクションを分離する必要があります。
SQL標準規格ではトランザクションの分離レベルを以下の4段階で定義しています。分離性は下に行くほど高くなります。
READ UNCOMMITTED
未コミット状態のデータであっても読み込む。
READ COMMITTED
コミットされたデータのみを読み込む。
REPEATABLE READ
トランザクション中は他のトランザクションでコミットされた更新を参照しないことで、同じレコードを繰り返し読んでも常に同じ結果が得られることを保証する。
SERIALIZABLE
トランザクションの逐次実行をエミュレートし、直列的に実行した場合と同じ結果になることを保証する。
トランザクションの分離レベルと、それによって制御できる不整合との関係はオンラインマニュアルなどでまとめられていますのでご覧になってください。
http://www.postgresql.jp/document/9.1/html/transaction-iso.html
PostgreSQLはいずれの分離レベルでも設定できるようになっています(ただし、'READ UNCOMMITTED'に設定しても内部的には'READ COMMITTED'と同じ動作となり、ダーティーリードという望ましくない結果が起きないようになっています)。分離レベルの設定は、SET TRANSACTIONコマンド、あるいはpostgresql.confのdefault_transaction_isolationなどで行うことが可能です。
演習
最後に
トランザクションの基本的な概念はつかめたでしょうか。
トランザクションの分離レベルに関しては、一概にどのレベルが最適と言うことはできません。トランザクションをまったく管理することなくそれぞれを並行して実行させると、処理パフォーマンスは向上しますが、上で挙げたような様々な不整合が発生するリスクも高まります。逆に、個々のトランザクションが干渉しないように順番よく実行するようにすると、データの整合性は保証されるものの、処理パフォーマンスの低下を招く可能性があります。同時実行性と分離性とはトレードオフの関係にありますので、バランスを考えて設定する必要があります。
機能的な面に目を向けますと、トランザクションの分離を実現するにはロックと呼ばれるメカニズムが使用されています。ロックと言うと、行レベルロック/テーブルレベルロックの使い分けや、「オススメ!OSS-DB情報 第12回」で取り上げているデッドロックの対策方法など、データベースを扱う上では理解しておきたいトピックが多々あります。興味のある方はこちらをご参照ください。
PostgreSQL日本語ドキュメント 第 13章同時実行制御
http://www.postgresql.jp/document/9.1/html/explicit-locking.html
オススメ!OSS-DB情報 第12回 デッドロックについて
http://www.oss-db.jp/measures/dojo_12.shtml
執筆:
平敷 兼貴 氏(OSS-DB Gold認定者)株式会社オークニー ジオソリューション事業部
FOSS4G(オープンソース地理空間ソフトウェア)を活用したアプリケーションやサービスの設計・開発に従事。
当連載の執筆を契機にPostgreSQLを再勉強し、2013年初めにOSS-DB Goldを取得。PostgreSQLマスターを目指して日々研鑽中。
アドバイザー:
森 亮 氏 株式会社オークニー代表取締役
2002年にオークニーを設立し、代表取締役に就任。マッピング・ GIS・LBS・ITS関連プロジェクトのビジネスコンサルティングを経て、現在は会社経営の傍ら、『入門Webマッピング』(Tyler Mitchell原著;オライリー・ジャパン発行)の翻訳、Open Source Geospatial財団日本支部の代表者を務めるなど、FOSS4G(オープンソース地理空間ソフトウェア)の普及活動に取り組んでいる。
監修:
松田 神一
© EDUCO All Rights Reserved.