【问题标题】:MySQL Under High Load, Race Condition?MySQL 在高负载、竞争条件下?
【发布时间】:2012-02-03 16:25:02
【问题描述】:

在我参与的应用程序中,我遇到了竞态条件的影响。情况如下,一般来说,负责一些繁重的应用逻辑的页面就是这样的格式:

test 中选择并确定是否有行已经与子句匹配。
如果匹配的行已经存在,我们将在此处终止,否则我们继续执行应用程序逻辑
将与我们初始选择匹配的值插入到test 表中。

通常,这可以正常工作并将操作限制为单次执行。但是,在有意同时发送许多请求的高负载和用户滥用情况下,MySQL 允许运行应用程序逻辑的多个实例,从而绕过 select 子句的限制。

它似乎实际上运行类似:
从测试中选择
从测试中选择
从测试中选择
(全部通过检查)
插入测试
插入测试
插入测试

我认为这样做是出于效率原因,但它对我的应用程序的上下文有严重的影响。我曾尝试使用 Get_Lock() 和 Release_Lock() 但这在高负载下似乎还不够,因为竞争条件似乎仍然存在。事务也是不可能的,因为应用程序逻辑非常繁重,并且涉及的所有表都不支持事务。

对于熟悉这种行为的任何人,是否可以关闭这种类型的处理,以便 MySQL 始终按照接收到的顺序处理查询?还有另一种方法可以使此类查询原子化吗?对此问题的任何帮助将不胜感激,我找不到有关此行为的太多记录。

【问题讨论】:

    标签: mysql


    【解决方案1】:

    这里的问题是,正如你推测的那样,你有一个竞争条件。

    SELECTINSERT 必须是一个原子 单元。

    您这样做的方式是通过事务。您不能安全地创建 SELECT,返回 PHP,并假设 SELECT 的结果将反映创建 INSERT 时的数据库状态。

    如果精心设计的交易(正确的解决方案)如你所说的那样不可能——我仍然强烈推荐它们——你将不得不做出最终的INSERT原子检查是否它的假设仍然正确(例如通过INSERT IF NOT EXISTS、存储过程或在应用程序中捕获INSERT 的错误)。如果不是,它将中止返回到您的 PHP 代码,这必须重新开始逻辑。

    顺便说一句,MySQL 可能正在按照接收到的顺序执行请求。多个同时连接可以接收SELECT A,SELECT B,INSERT A,INSERT B。因此,唯一的“解决方案”是一次只允许一个连接 - 这会扼杀你的可扩展性。

    【讨论】:

    • 谢谢,这似乎是一个可能的解决方案。如果假设仍然正确,人们将如何进行原子检查。如果通过逻辑顶部的简单选择,是否可能新数据仍然不会反映在表格中?
    • @majicbunnie 你让第二个INSERT 成为一个存储过程,它执行BEGIN TRANSACTION; SELECT; [check]; INSERT; END TRANSACTION;
    • 谢谢,我认为这对我有用,尽管我可能需要更改一些存储引擎类型以支持事务。据我了解,它们都必须具备交易能力,这样的解决方案才能发挥作用。
    【解决方案2】:

    就个人而言,我会以另一种方式进行检查。

    尝试插入行。如果失败,那么那里已经有一行了。

    通过这种方式,您可以在单个查询中检查或复制并插入新行,从而消除竞争的可能性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-23
      • 2018-10-08
      相关资源
      最近更新 更多