【问题标题】:Mysql performance update in()Mysql 性能更新 in()
【发布时间】:2019-01-30 01:43:53
【问题描述】:

我遇到了 UPDATE WHERE id IN() 的性能问题。 伪代码是(归结为):

  1. 从 PHP 开始事务:

    $pdoAdapter->beginTransaction();
    
  2. 查询 MySQL 以获取来自外部世界并存储在数据库中且应开具发票的价格元素

    SELECT id, price 
    FROM incoming_price_elements 
    WHERE <advanced where> 
    FOR UPDATE
    
  3. 获取新的发票 ID

    SELECT id 
    FROM invoice_id_counter 
    FOR UPDATE; 
    UPDATE invoice_id_counter SET id=id+1;
    
  4. 在 PHP 中创建包含大量汇总和其他聚合的发票并存储结果

    INSERT INTO invoices (id, total_price)
    VALUES (<id from 3.>, <total price summed>);
    
  5. 更新所有incoming_price_elements 以将它们标记为发票

    UPDATE incoming_price_elements 
    SET invoice_id=<id from 3.> 
    WHERE id IN (<all ids selected in 2.>);
    
  6. 提交


我的问题是第 5 步非常慢(在几秒钟内)并且它在第 3 步中阻塞了 id 计数器。 要传输的id数量超过10.000个id,并且id是主键。

关于如何优化它的任何建议?我正在考虑创建一个临时表并选择其中的所有 id,但我对临时表的经验为零。

【问题讨论】:

  • 是否有理由自己增加 id 而不是使用AUTO_INCREMENT?您是否需要其他表格中的发票 ID(因此在执行第 5 步之前需要知道该 ID)?

标签: php mysql optimization


【解决方案1】:

你可以使用 INNER JOIN 代替 IN 子句

  UPDATE incoming_price_elements a
  INNER JOIN (
      SELECT id
      FROM incoming_price_elements 
      WHERE  <advanced where> 

  ) t ON t.id = a.id 
  SET invoice_id= <id from 3.>

这应该会提高您在这部分“交易”中的表现

【讨论】:

  • 这是个好建议;但我担心在 4 中的计算过程中已将新行添加到 incoming_price_elements,因此结果不会完全相同
  • 如果您..正确使用事务..或使用触发器,您可以管理以避免这种情况发生,这不会发生....您还可以使用适当的高级执行子查询在哪里可以避免您担心的情况.. INNER JOIN 的执行速度更快,但也可以一次性执行,而不是对相同数据进行严重迭代
  • 我不确定如何避免在事务中添加数据?使用 InnoDB 我不做表锁定,只做行锁定?
  • 试试看触发器 .. .. .. 但对我来说似乎与使用 IN 子句提高性能无关
【解决方案2】:

根据您的一个 cmets,数据“不断”添加到 incoming_price_elements?那张桌子有点像“集结区”?还有一个问题是,如果在你完成处理之前添加了新项目,那么有麻烦吗?

如果是这样,那么让我们翻转。

CREATE TABLE next_icp LIKE incoming_price_elements;
RENAME TABLE incoming_price_elements TO prev_icp,
             next_icp TO incoming_price_elements;   -- swap in an empty table
-- now process `prev_icp` as discussed already.

至于迟钝,让我们尝试在开始交易之前进行更多处理

您能否在incoming_price_elements 中添加一些额外的列来处理“大量求和和其他聚合”?之后开始事务,做其他事情,最后(正如 scaisEdge 建议的那样)做一个“多表更新”(使用JOIN)而不是IN

如果这仍然太具有侵略性,那么一次执行 100 个 ids,而不是一次全部执行。 UPDATE 必须保存旧行以防崩溃(等);这将是昂贵的。通过打破它;你让其他连接完成一些工作。

这是围绕步骤 1..6 循环,一次只关注 100 行,直到您用完 staging table。这会让其他id被夹在中间;我希望没问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-21
    • 2018-05-16
    • 2015-07-29
    • 2016-03-16
    • 1970-01-01
    相关资源
    最近更新 更多