MySQL中的'FOR UPDATE'命令无法正常工作

6

我有两个PHP页面,page1.php和page2.php

page1.php

execute_query('START TRANSACTION');
$res =execute_query('SELECT * FROM table WHERE id = 1 FOR UPDATE');

sleep(20);

print $res->first_name;
execute_query('COMMIT');

print"\n OK";

page2.php

 $res =execute_query('SELECT * FROM table WHERE id = 1');
print $res->first_name;

我几乎同时执行这两个页面。
所以根据mysql的"FOR UPDATE"条件,page2.php中的结果将只在page1.php执行之后显示(即在page1.php中显示"OK"之后),因为两个页面都在读取同一行。
但事实是, page2.php突然显示了结果,甚至在page1.php执行完成之前就出现了。
请问"FOR UPDATE"命令出了什么问题?

考虑到你说“突然显示结果”,我会假设页面2不仅仅是加载,而是在显示结果前等待了一段时间。如果是这样的话,我的猜测是MYSQL没有保持连接开放20秒钟(你的睡眠时间),当连接断开时锁被释放。 - Hugo Delsing
@HugoDelsing,感谢您的评论。我不认为page2.php需要等待几秒钟。我尝试了id=2的page2.php,花费相同的时间。 - Linto P D
@HugoDelsing 如果我在两个页面的顶部添加execute_query('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'),那么它就可以正常工作。不知道具体发生了什么,但它能够正常工作。 - Linto P D
2个回答

3
我假设该表是InnoDB(不是MyISAMMEMORY)。 您在事务中使用了SELECT。我不知道您的隔离级别,但我猜测您的事务互不阻塞。
有关详细信息,请参见此页面:http://dev.mysql.com/doc/refman/5.5/en/set-transaction.html 编辑: 我要更好地解释这个概念,如所请求的那样。 隔离级别是一个会话/全局变量,确定事务执行的方式。某些隔离级别在尝试修改相同行时会阻止其他事务,但某些隔离级别则不会。
例如,如果您使用UNCOMMITTED,它不会阻止任何内容,因为您访问行的实际版本(这可能会在事务结束之前过时)。 另一个SELECT(page2)仅读取表格,因此无需等待第一个事务结束。 SERIALIZABLE更安全。它不是默认值,因为它是最慢的隔离级别。如果您使用它,请确保FOR UPDATE对您仍然有意义。

谢谢您的评论。我正在使用“InnoDB”。您能解释一下您的答案与我的示例有关吗? - Linto P D
如果我在两个页面的顶部添加execute_query('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE');,那么它就可以正常工作。不知道具体发生了什么,但它能够正常工作。 - Linto P D
好的,我编辑了答案。是的,SERIALIZABLE 应该是正确的选择! - Federico Razzoli

-1

我认为你的SELECT FOR UPDATE语句在BEGIN TRANSACTION中,因此它不会锁定记录,直到达到COMMIT语句,并且你使用了sleep(20)延迟执行。所以page2.php将被执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接