场景非常简单。我们有一个 PHP 脚本:
- 执行 MySQL 查询
GET_LOCK('TEST', 0)
- sleep 5 秒
- 执行 MySQL 查询
RELEASE_LOCK('TEST')
打开网络浏览器并在 2 个单独的选项卡中调用上述脚本,但调用之间有 1-2 秒的延迟。
- 预期结果:第一次调用获取“TEST”锁,第二次调用未能获取“TEST”锁。
- 实际结果:第一次和第二次调用都获取了 TEST 锁。
持久连接在 PHP.INI 中被禁用,并且没有使用特定的连接选项来启用它们。 MySQL 和 MySQLi 扩展都会产生相同的结果。在两种设置上进行测试:PHP 5.3.9 + MySQL 5.5.20; PHP 5.4.4 + MySQL 5.5.35。
奇怪的是,通过终端、工作台甚至不同的浏览器执行相同的场景实际上可以按预期工作。我只能通过在同一浏览器中调用脚本来重现该问题(无论具体是哪一个,Opera、Firefox、Chrome 等)。
我找到了Bug #62002 MySQL 网站上描述了完全相同的问题,但从未解决或解释。错误报告中建议执行SELECT @@pseudo_thread_id
,在我的例子中,它有时会产生相同的数字,有时会产生不同的数字。
我怀疑两个出于某种奇怪原因的调用都是在同一个物理连接上执行的,这可以解释为什么脚本可以获得相同的锁。他们将使用相同的 MySQL session ,并且在这种情况下调用 GET_LOCK
两次将会成功。
有什么想法为什么会发生这种情况或如何预防它吗?
最佳答案
我想我已经了解了这一点......
无论您相信与否,这种奇怪的行为都是由网络浏览器引起的,更令人震惊的是所有经过测试的浏览器(Opera、Chrome、Firefox)。事实证明,浏览器会执行某种特殊处理来加载具有相同 URL 的页面。
场景:假设我们正在两个单独的选项卡中加载脚本http://localhost/test.php
,彼此之间间隔 2 秒。想象一个简单的脚本,输出当前时间,休眠 10 秒,然后再次输出当前时间。
预期结果:每个选项卡都异步向服务器发出请求,第二个选项卡的执行时间比第一个选项卡晚 2 秒。每个选项卡将花费 10 秒来完成执行,相对于第一个选项卡,第二个选项卡会晚 2 秒完成。选项卡将显示不同的输出(打印时间将偏移 2 秒)。
实际结果 A (Opera v12):仅向服务器发出 1 个请求(通过服务器访问日志验证)。第一个选项卡需要 10 秒才能完成执行,第二个选项卡也立即完成(不是 2 秒后)。选项卡显示相同的输出(打印时间相同)。
实际结果 B(Firefox、Chrome、Opera v20):每个选项卡均按预期向服务器发出请求,但请求是同步。第一个选项卡需要 10 秒才能完成执行,第二个选项卡需要额外 10 秒,在第一个选项卡后 10 秒完成。选项卡将显示不同的输出(打印时间将偏移 10 秒)。
解决方法:对 URL 进行简单的人为更改即可解决该问题。例如,执行
http://localhost/test.php
在一个选项卡中,http://localhost/test.php?
(已添加问号)在另一个选项卡中。
观察:在脚本中设置各种缓存 header 不会影响结果。使用默认浏览器配置,无需调整。后续测试让大家松了一口气,Internet Explorer v10 产生了预期的结果(令人惊讶),这使它成为唯一真正按预期工作的浏览器,谁会想到呢!
根据这一发现,现在可以理解为什么 MySQL/PHP 中的 GET_LOCK
似乎行为不当,这实际上是由网络的完全意外行为引起的浏览器。
关于php - 当不应该通过 PHP MySQL/MySQLi 有两个(应该是)单独的连接时,GET_LOCK 会成功,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22965833/