PHP+MySQL高同時ロックトランザクションの問題解決方法

2322 ワード

この例では、PHP+MySQLの高同時ロックトランザクションの問題解決方法について説明します.皆さんの参考にしてください.具体的には以下の通りです.
1、背景:
データを挿入する際、testテーブルにusernameが「mraz」であるデータがあるかどうかを判断し、ない場合は挿入し、ある場合は「挿入済み」と提示し、usernameが「mraz」であるレコードを1つだけ挿入することを目的としている.
2、一般的なプログラムロジックは以下の通りである.

$conn = mysqli_connect('127.0.0.1', 'root', '111111') or die(mysqli_error());
mysqli_select_db($conn, 'mraz');
$rs = mysqli_query($conn, 'SELECT count(*) as total FROM test WHERE username = "mraz" ');
$row = mysqli_fetch_array($rs);
if($row['total']>0){
  exit('exist');
}
mysqli_query($conn, "insert into test(username) values ('mraz')");
var_dump('error:'.mysqli_errno($conn));
$insert_id = mysqli_insert_id($conn);
echo 'insert_id:'.$insert_id.'
'; mysqli_free_result($rs); mysqli_close($conn);

3、一般的に少量の要求の場合、プログラムロジックに問題はありません.しかし、高同時要求が実行されると、プログラムは予想通りに実行されず、複数のusernameが「mraz」であるレコードが挿入される.
4、解決策:mysqlのFOR UPDATE文と取引の隔離性を利用する.FOR UPDATEはInnoDBにのみ適用され、トランザクション(BEGIN/COMMIT)で有効になる必要があります.
コードを調整すると、次のようになります.

$conn = mysqli_connect('127.0.0.1', 'root', '111111') or die(mysqli_error());
mysqli_select_db($conn, 'mraz');
mysqli_query($conn, 'BEGIN');
$rs = mysqli_query($conn, 'SELECT count(*) as total FROM test WHERE username = "mraz" FOR UPDATE');
$row = mysqli_fetch_array($rs);
if($row['total']>0){
  exit('exist');
}
mysqli_query($conn, "insert into test(username) values ('mraz')");
var_dump('error:'.mysqli_errno($conn));
$insert_id = mysqli_insert_id($conn);
mysqli_query($conn, 'COMMIT');
echo 'insert_id:'.$insert_id.'
'; mysqli_free_result($rs); mysqli_close($conn);

5、phpのcurlシミュレーション高同時要求phpスクリプトを再利用し、データベースにusernameが「mraz」のレコードが1つしかないことを確認する.プログラム実行の予想結果に達する~
PHPに関する詳細について興味のある読者は、「php+mysqlデータベース操作入門チュートリアル」、「php+mysqliデータベースプログラム設計テクニック総括」、「phpオブジェクト向けプログラム設計入門チュートリアル」、「PHP配列(Array)操作テクニック大全」、「php文字列(string)用法総括」および「phpよくあるデータベース操作テクニック要約」を参照してください.
ここで述べたことが皆さんのPHPプログラム設計に役立つことを願っています.