php - 锁定 PHP 以实现关键部分 - MySQL 的意外结果

标签 php mysql synchronization transaction-isolation locks

目标是让 PHP 脚本的代码的特定部分一次只能由一个线程/进程执行。

一些限制:

  • 信号量在我的系统上不可用
  • 手册中提到在多线程服务器中不能依赖 flock(),因此 flock() 已过时。 (确认了这一点)

我认为使用 MySQL 进行同步是可能的(为什么不呢?),但我得到了意想不到的结果。

为了测试,我用脚本 test.php 打开了两个浏览器选项卡

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 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'
) 

I go to the first browser tab and access the script. I wait 5 seconds, go to the other tab and access the script so that both scripts run in parallel. I expect the first tab to wait, and the 2nd tab should exit after the 1st query. However, both tabs wait on the sleep() line. This means that the first query always returns 'NOT_RUNNING' .

Some odd things:

  • When I repeat the above experiment, I run both tabs in FireFox, and then in a 3rd different browser type, say Chrome, then it works! (status is set to RUNNING while the sleeping, script exits early when status is RUNNING )
  • When I repeat the above experiment using two different command line windows, and run the script from the command line, it works!
  • I check phpMyAdmin while it is waiting, status gets updated correctly.

I have tried everything, locking the table, transactions, SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED, SET autocommit=1;, yet always the same result.

Does anyone know what is happening here? Is there any good solution for this problem?

Systems tested: - Win, MySQL 5.0, php 5.3 - Linux, MySQL 5.0.91-community-log, php 5.2.12

I'm totally stuck here, thanks for taking a look!


UPDATE:

Thanks for submitting your answers - I still cannot solve this problem. Here is the code with GET_LOCK() and session_write_close() as suggested: I have also tried row level locking, transactions and different isolation levels. Perhaps it cannot be done?

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();

最佳答案

第二个浏览器窗口没有卡在 sleep 命令上。它正在阻塞,因为 session 正在阻塞(因此它试图打开同一个 session )。如果您不再需要它(因此不希望它阻塞),您可以使用 session_write_close 关闭当前 session 。 ...

关于php - 锁定 PHP 以实现关键部分 - MySQL 的意外结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3558989/

相关文章:

php - 合并两个 mysql 字段并使用 php 更新第三个字段

MySQL 忽略删除查询的强制索引,但不忽略等效的选择查询

mysql - 从 web2py 中的 Controller 更新记录

c# - 使用 DirectShow.NET 的音频同步问题

cookies - Cookie 同步 : user id mapping between different cookie domains

Php SoapClient 超时(ubuntu)

php - amazon-s3-php-class - 如何更改权限

php - MySqli:是否可以创建数据库?

php - 单击 +1 增加 MySQL 值

java - 所有java线程都在单个核心上运行,最终花费太多时间来执行