@see https://www.websec-room.com/2015/11/17/2356
わかりやすくまとまっています(ㆁᴗㆁ✿)
<?php require_once("function.php"); try{ $dbh = new PDO(DSN, USERNAME, PASSWORD); // 静的プレースホルダを指定 $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); // エラー発生時に例外を投げる $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //パラメータ $id = 1; //トランザクション処理を開始 $dbh->beginTransaction(); try { //プリペアドステートメント / ロック $stmt1 = $dbh->prepare("SELECT * FROM USERS WHERE ID = ? FOR UPDATE"); //$stmt1 = $dbh->prepare("SELECT * FROM USERS WHERE ID = ? LOCK IN SHARE MODE”); //不整合が起きる $stmt1->bindParam(1, $id, PDO::PARAM_INT); $stmt1->execute(); //プリペアドステートメント $stmt = $dbh->prepare("UPDATE USERS SET COUNT = COUNT + 1 WHERE ID = ?"); $stmt->bindParam(1, $id, PDO::PARAM_INT); $stmt->execute(); //コミット $dbh->commit(); }catch(PDOException $e){ //ロールバック $dbh->rollback(); throw $e; } } catch(PDOException $e){ echo $e->getMessage(); } ?>
また、トランザクション分離レベルは以下のようになります(MySQL)。
ダーティーリード ファジーリード ファントムリード READ UNCOMMITED 起きる 起きる 起きる READ COMMITTED 起きない 起きる 起きる REPEATABLE READ 起きない 起きない 起きない SERIALIZABLE 起きない 起きない 起きない ※ REPEATABLE READ のファントムリードが起きないのは、MySQL の仕様
※ MySQL のデフォルトトランザクション分離レベルは REPEATABLE READ
※ ANSI/ISO SQL の既定では、REPEATABLE READ でファントムリードは起きる
InnoDB+REPEATABLE READの場合はファントムリードは起きない。
上記にはないけれど、ロールバック後のリトライ処理も必要
<?php $retries = 3; while ($retries > 0) { try { $dbh = new PDO("mysql:host=localhost;dbname=blahblah", $user, $pass); // Do query, etc. $retries = 0; } catch (PDOException $e) { // Should probably check $e is a connection error, could be a query error! echo "Something went wrong, retrying..."; $retries--; usleep(500); // Wait 0.5s between retries. } }
もくじ
ACID特性
@see http://d.hatena.ne.jp/fat47/20140212/1392171784
トランザクション処理に求められる4つの特性です。
原子性 (Atomicity)
トランザクションに含まれる手順が「すべて実行されるか」「すべてされないか」のどちらかになる性質。
一貫性 (Consistency)
どんな状況でもトランザクション前後でデータの整合性が矛盾なく保たれる性質。
分離性 (Isolation)
トランザクション実行中は、処理途中のデータは外部から隠蔽されて他の処理に影響を与えない性質。
永続性 (Durability)
トランザクションが完了したら、システムがクラッシュしてもデータが消失することがない性質。
ダーティリード、ファジーリード、ファントムリードなど解説もわかりやすい。
@see http://d.hatena.ne.jp/fat47/20140212/1392171784
分離レベルに関しての選択として、
InnoDB + REPEATABLE READ
でいいはず。