首页 文章

锁定PHP以实现一个政治部分 - MySQL的意外结果

提问于
浏览
1

目标是让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']." \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' ";
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秒,转到另一个选项卡并访问脚本,以便两个脚本并行运行 . 我希望第一个标签等待,第二个标签应该在第一个查询后退出 . 但是,两个标签都在sleep()行上等待 . 这意味着第一个查询始终返回'NOT_RUNNING' .

一些奇怪的事情:

  • 当我重复上述实验时,我在FireFox中运行两个选项卡,然后在第三种不同的浏览器类型中运行,比如Chrome,那么它可以运行! (当状态为RUNNING时,状态设置为RUNNING,而睡眠状态脚本会提前退出)

  • 当我使用两个不同的命令行窗口重复上述实验,并从命令行运行脚本时,它可以工作!

  • 我在等待时检查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']." \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 回答

  • 0

    第二个浏览器窗口未挂在 sleep 命令上 . 它's blocking because sessions are blocking (and hence it'试图打开同一个会话) . 您可以使用session_write_close关闭当前会话,如果您不希望它阻止)

  • 2

    Mysql有一个名为GET_LOCK的函数 .

相关问题