@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
でいいはず。


