目標は、一度に 1 つのスレッド/プロセスによってのみ実行できるコードの特定のセクションを含む PHP スクリプトを作成することです。
いくつかの制限:
- 私のシステムではセマフォが利用できません
- マニュアルには、マルチスレッド サーバーでは flock() を使用できないと記載されているため、flock() は廃止されました。(これを確認した)
そのため、MySQL を同期に使用することは可能であると考えていましたが (なぜでしょうか?)、予期しない結果が得られています。
テストするために、スクリプト test.php で 2 つのブラウザー タブを開きます。
mysql_connect($dbhost, $dbusername, $dbpassword);
mysql_select_db($database_name);
$sql = "SELECT my_val FROM `my_table` WHERE `my_var`='status' ";
$result = mysql_query($sql)or die(mysql_error());
$row = mysql_fetch_array($result, MYSQL_ASSOC);
echo "status is:".$row['my_val']."<br>\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<br>';
// leave critical section. let the others know
$sql = "UPDATE `my_table` SET `my_val`='NOT_RUNNING' WHERE `my_var`='status' ";
mysql_query ($sql);
表は次のとおりです。
CREATE TABLE `my_table` ( `my_var` varchar(255) NOT NULL デフォルト '', `my_val` varchar(255) NOT NULL デフォルト '', 主キー (`my_var`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO `my_table` 値 ( 「ステータス」、「NOT_RUNNING」 )
最初のブラウザー タブに移動し、スクリプトにアクセスします。5 秒待ってから、別のタブに移動してスクリプトにアクセスし、両方のスクリプトが並行して実行されるようにします。最初のタブが待機することを期待しており、2 番目のタブは最初のクエリの後に終了する必要があります。ただし、両方のタブが sleep() 行で待機します。これは、最初のクエリが常に 'NOT_RUNNING' を返すことを意味します。
いくつかの奇妙なこと:
- 上記の実験を繰り返すと、両方のタブを FireFox で実行し、次に 3 番目の異なるブラウザー タイプ (たとえば Chrome) で実行すると、うまくいきます。(ステータスはスリープ中に RUNNING に設定され、ステータスが RUNNING の場合、スクリプトは早期に終了します)
- 2 つの異なるコマンド ライン ウィンドウを使用して上記の実験を繰り返し、コマンド ラインからスクリプトを実行すると、機能します。
- 待機中にphpMyAdminをチェックすると、ステータスが正しく更新されます。
テーブルのロック、トランザクション、SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED、SET 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']."<br>\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<br>';
// 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();