Webセキュリティの小部屋

Twitter のフォローはこちらから Facebook ページはこちら Google+ページはこちら RSSフィードのご登録はこちらから
公開日:2015年11月17日

PHP + PDO + MySQL のトランザクション制御方法まとめ

はじめに

ネットで調査していたのですが、PHP + PDO + MySQL の組み合わせのトランザクション制御をまとめてある記事が見当たりませんでした。

ですので、この記事では、PHP + PDO + MySQL の組み合わせのトランザクション制御をまとめて紹介いたします。

トランザクション制御とは

トランザクション制御とはいいますが、そもそもトランザクション制御とは何でしょうか。

簡単に言ってしまえば、データの更新時に、成功したらデータを更新して、失敗したらデータを元に戻し、データが壊れないようにすることです。

これらの性質は、原子性 (atomicity)、一貫性 (consistency)、分離性 (isolation)、持続性 (durability) の頭文字をとって「ACID」と呼ばれています。

ACID はデータを守るためのデータベース(RDBMS) の基本的な仕組みです。

PHP + PDO + MySQL でトランザクション制御を行う方法

基本的に以下の3つのメソッドを使用します。

各メソッドの内容は、トランザクションを開始する PDO::beginTransaction、更新を確定する PDO::commit、失敗時に更新をなかったことにする PDO::rollBack になります。

これを具体的な PHP のコードで表すと以下のようになります。

・function.php
function.php は各サンプルコードで共通の設定になります。

・transaction.php
transaction.php では、try ブロックを2重にしてあります。2重にする理由は、データ更新時の成功か失敗かでコミットするかロールバックするか区別できるようにするためです。

トランザクション分離レベル(Isolation Level)

トランザクションには、トランザクション分離レベルというものがあります。

トランザクション分離レベルによって、トランザクションが複数同時に処理された時の動作が変わってきます。

まず、トランザクションの同時実行時のリードの種類には以下のものがあります。

  • ダーティーリード
    • 他のトランザクションのセッションでコミットされていない更新データを読み込む
  • ファジーリード
    • 他のトランザクションのセッションでコミットした更新データを読み込む。
  • ファントムリード
    • 他のトランザクションのセッションが追加したデータを読み込む

また、トランザクション分離レベルは以下のようになります(MySQL)。

ダーティーリード ファジーリード ファントムリード
READ UNCOMMITED 起きる 起きる 起きる
READ COMMITTED 起きない 起きる 起きる
REPEATABLE READ 起きない 起きない 起きない
SERIALIZABLE 起きない 起きない 起きない

※ REPEATABLE READ のファントムリードが起きないのは、MySQL の仕様
※ MySQL のデフォルトトランザクション分離レベルは REPEATABLE READ
※ ANSI/ISO SQL の既定では、REPEATABLE READ でファントムリードは起きる

PHP + PDO から MySQL にトランザクション分離レベルを設定するには、以下のように行います。サンプルコードには、READ COMMITTED を指定していますが、この部分を書き換えることで制御を変更することができます。

・isolation.php

データロック

複数の処理が同時にトランザクションでデータに更新を行うと、データに不整合が発生してしまいます。

そのために、MySQL では SQL で以下のロックを提供しています。

  • 共有(s)ロック
    • トランザクションによる行の読み取りが許可される
  • 排他(x)ロック
    • トランザクションによる行の更新または削除が許可される

トランザクションの SQL で、SELECT … LOCK IN SHARE と宣言することで、共有ロックをかけることができます。他のセッションも読み取ることができますが、最初のトランザクションがコミットするまで変更することができません。

トランザクションの SQL で、 SELECT … FOR UPDATE と宣言することで、排他ロックをかけることができます。排他ロックがかかっている間は、他のロックはブロックされます。

トランザクションがコミットまたはロールバックされると、全てのロックはクリアされます。

なお、カウントアップなどの処理では、排他ロックが必要なので、SELECT … FOR UPDATE を使用する必要があります。SELECT … LOCK IN SHARE では不整合が発生するのでご注意ください。

実際のサンプルコードは以下のようになります。

・lock.php

おわりに

駆け足でしたが、PHP + PDO + MySQL のトランザクション制御方法を網羅的に紹介できたと思うのですが、いかがだったでしょうか。

トランザクション制御は、簡単なようですが、デッドロックを起こさない事も含め考慮点が多いです。

この記事が、PHP + PDO + MySQL のトランザクション制御方法のサンプルになり、みなさんのいろいろな調査の負担を減らすことができれば幸いです。

参考サイト


スポンサーリンク




カテゴリー:ブログ

Twitter でも、いろんな情報を発信しています。



コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA