PHPでロックしてCRTITICALセクションをACHEIVE -MYSQLで予期しない結果
-
01-10-2019 - |
質問
目標は、一度に1つのスレッド/プロセスでのみ実行できるコードの特定のセクションを備えたPHPスクリプトを使用することです。
いくつかの制限:
- 私のシステムでは使用できません
- マニュアルは、Flock()をマルチスレッドサーバーに依存できないため、Flock()が外出することを述べています。 (これを確認しました)
だから、同期にMySQLを使用することは可能だと思った(なぜですか?)が、予期しない結果が得られています。
テストするために、スクリプトtest.phpで2つのブラウザタブを開いています
mysql_connect($ dbhost、$ dbusername、$ dbpassword); mysql_select_db($ database_name);
$ sql = "my_valを選択します my_table
どこ my_var
= 'status' "; $ result = mysql_query($ sql)またはdie(mysql_error()); $ row = mysql_fetch_array($ result、mysql_assoc); echo" status is: "。$ row ['my_val']。」
n "; mysql_free_result($ result);
if($ row ['my_val'] == 'running')die( 'プロセスはすでに実行されている!');
//クリティカルセクションの入力
$ sql = "更新 my_table
セットする my_val
= 'running' where my_var
= 'status' "; mysql_query($ sql);
睡眠(10); //ここでいくつかの作業をしてください。 Echo '完成した作業
';
//クリティカルセクションを離れます。他の人に$ sql = "更新を知らせます my_table
セットする my_val
= 'not_running' where my_var
= 'status' "; mysql_query($ sql);
テーブルは次のとおりです。
CREATE TABLE `my_table` ( `my_var` varchar(255) NOT NULL default '', `my_val` varchar(255) NOT NULL default '', PRIMARY KEY (`my_var`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO `my_table` VALUES ( 'status', 'NOT_RUNNING' )
最初のブラウザタブに移動し、スクリプトにアクセスします。 5秒待って、他のタブに移動してスクリプトにアクセスして、両方のスクリプトが並行して実行されるようにします。最初のタブが待機することを期待し、2番目のタブは最初のクエリの後に終了するはずです。ただし、両方のタブはSleep()ラインを待ちます。これは、最初のクエリが常に「not_running」を返すことを意味します。
いくつかの奇妙なこと:
- 上記の実験を繰り返すと、両方のタブをFirefoxで実行し、次に3つの異なるブラウザータイプのChromeで実行してください。 (ステータスは睡眠中に実行されるように設定されています、スクリプトが実行されているときに早期に終了します)
- 2つの異なるコマンドラインウィンドウを使用して上記の実験を繰り返し、コマンドラインからスクリプトを実行すると、機能します!
- PHPMyAdminが待っている間に確認し、ステータスが正しく更新されます。
私はすべてを試して、テーブルのロック、トランザクション、グローバルトランザクションの分離レベルを設定していない、autocommit = 1;を設定しますが、常に同じ結果です。
誰かがここで何が起こっているのか知っていますか?この問題の良い解決策はありますか?
テスト済みのシステム:-Win、MySQL 5.0、PHP 5.3 -Linux、MySQL 5.0.91 -Community -Log、PHP 5.2.12
私はここで完全に立ち往生しています、見てくれてありがとう!
アップデート:
あなたの回答を提出してくれてありがとう - 私はまだこの問題を解決することはできません。提案されているように、get_lock()とsession_write_close()のコードは次のとおりです。また、行レベルのロック、トランザクション、異なる分離レベルも試しました。おそらくそれはできませんか?
session_write_close(); mysql_connect($dbhost, $dbusername, $dbpassword); mysql_select_db($database_name); $sql = "CREATE TABLE IF NOT EXISTS `my_table` ( `my_var` varchar(255) NOT NULL default '', `my_val` varchar(255) NOT NULL default '', PRIMARY KEY (`my_var`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; "; mysql_query($sql)or die(mysql_error()); mysql_query ("SELECT get_lock('my_lock', 100)"); $sql = "SELECT my_val FROM `my_table` WHERE `my_var`='status' "; $result = mysql_unbuffered_query($sql)or die(mysql_error()); $row = mysql_fetch_array($result, MYSQL_ASSOC); echo "status is:".$row['my_val']."
\n"; mysql_free_result($result); if ($row['my_val']=='RUNNING') die ('process is already running!'); // entering critical section $sql = "UPDATE `my_table` SET `my_val`='RUNNING' WHERE `my_var`='status' "; mysql_query ($sql); sleep(10); // do some work here. echo 'finished work
'; // leave critical section. let the others know //$sql = "UPDATE `my_table` SET `my_val`='NOT_RUNNING' WHERE `my_var`='status' "; $sql = "REPLACE INTO `my_table` (`my_var`,`my_val`) VALUES ('status', 'NOT_RUNNING')"; mysql_query ($sql); $result = mysql_query($sql)or die(mysql_error()); mysql_query ("SELECT release_lock('my_lock')"); die();
解決
2番目のブラウザウィンドウがぶら下がっていません sleep
指図。セッションがブロックされているため、ブロックしています(したがって、同じセッションを開こうとしています)。あなたがもうそれを必要としない場合(したがってそれをブロックしたくない)、あなたは現在のセッションを閉じることができます) session_write_close
...
他のヒント
MySQLには呼ばれる関数があります get_lock.