锁定以实现关键部分-MySQL产生意外结果

问题描述 投票:1回答:2

目标是使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);

表为:

创建表my_table(`my_var` varchar(255)NOT NULL默认值'',`my_val` varchar(255)NOT NULL默认值'',主键(`my_var`))ENGINE = InnoDB DEFAULT CHARSET = latin1;插入`my_table`值('状态','NOT_RUNNING')

我进入第一个浏览器选项卡并访问脚本。我等待5秒钟,转到另一个选项卡并访问该脚本,以便两个脚本并行运行。我希望第一个选项卡等待,第二个选项卡应在第一个查询后退出。但是,两个选项卡都在sleep()行上等待。这意味着第一个查询始终返回“ NOT_RUNNING”。

一些奇怪的事情:

  • [当我重复上述实验时,我在FireFox中运行两个选项卡,然后在第3种不同的浏览器类型(例如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']."<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();
php mysql synchronization transaction-isolation locks
2个回答
2
投票

第二个浏览器窗口没有挂在sleep命令上。它之所以受阻,是因为会话受阻(因此它试图打开同一会话)。如果您不再需要当前会​​话(因此不希望其阻塞),则可以使用session_write_close ...

关闭当前会话

0
投票

MySQL具有称为session_write_close的功能。

© www.soinside.com 2019 - 2024. All rights reserved.