【问题标题】:PHP MySQL - Update 6.5m rows performance issuesPHP MySQL - 更新 650 万行性能问题
【发布时间】:2017-09-06 11:35:07
【问题描述】:

我正在使用 MySQL 表,我需要为每一行增加一个列中的值,其中有超过 6.5m。

col 类型为varchar,可以包含整数或字符串(即+1)。表类型为MyISAM

我已经用 PHP 尝试过:

  $adjust_by = 1;
  foreach ($options as $option) {
      $original_turnaround = $option['turnaround'];
      $adjusted_turnaround = $option['turnaround'];

      if (preg_match('/\+/i', $original_turnaround)) {
        $tmp = intval($original_turnaround);
        $tmp += $adjust_by;
        $adjusted_turnaround = '+'.$tmp;
      } else {
        $adjusted_turnaround += $adjust_by;
      }

      if (!array_key_exists($option['optionid'], $adjusted)) {
        $adjusted[$option['optionid']] = array();
      }

      $adjusted[$option['optionid']][] = array(
        'original_turn' => $original_turnaround,
        'adjusted_turn' => $adjusted_turnaround
      );
  }//end fe options

  //update turnarounds:
  if (!empty($adjusted)) {
    foreach ($adjusted as $opt_id => $turnarounds) {
      foreach ($turnarounds as $turn) {
        $update = "UPDATE options SET turnaround = '".$turn['adjusted_turn']."' WHERE optionid = '".$opt_id."' and turnaround = '".$turn['original_turn']."'";
        run_query($update);
      }
    }
  }

由于显而易见的原因,这种方法存在严重的性能问题。在我的本地开发环境中运行它会导致大量错误并最终导致服务器崩溃。

我需要考虑的另一件事是在生产环境中运行时。这是针对电子商务商店的,我不能进行像这样的大规模更新锁定数据库或导致任何其他问题。

我发现的一个可能的解决方案是:Fastest way to update 120 Million records

但是创建另一个表有它自己的问题。代码库的状态不是很好,类似的查询在这个表的很多地方运行,所以我必须修改大量的查询和文件才能使这种方法起作用。

我有哪些选择(如果有的话)?

【问题讨论】:

  • “我需要考虑的另一件事是当它在生产环境中运行时。这是针对电子商务商店的,我不能进行像这样的巨大更新锁定数据库或导致任何其他问题。 " MyISAM 引擎会在更新或插入时锁定表...您应该考虑将表转换为 InnoDB 引擎。此引擎不会通过更新或插入锁定表。
  • 表的索引是否正确?也许简单的索引就足以加快速度。
  • 不要使用 PHP 运行密集型任务。您可能会遇到最大超时问题,而且 PHP 不是为 CPU 密集型任务而设计的。你可以使用 python 来完成任务。
  • @RaymondNijland 是的,您说的完全正确,但是,更改表类型是否会对该表与另一个表连接的其他查询产生任何影响?
  • @user254153 您是否建议切换到 Python,因为您认为与 PHP 相比,它对于 CPU 密集型任务“更好”?请更新您的知识库,您的建议是错误的、不正确的,并且基于您的个人喜好而不是事实。此外,这不是 CPU 密集型任务。

标签: php mysql performance myisam


【解决方案1】:

您可以使用 SQL 完成此任务。

  • 使用CAST,您可以将字符串转换为整数。
  • 使用IFSUBSTR,您可以检查字符串是否包含+
  • 使用CONCAT,您将添加(将两个值合并为一个字符串)+ 到您的计算结果(如果需要)。

试试这个 SQL:

"UPDATE `options` SET `turnaround` = CONCAT(IF(SUBSTR(`turnaround`, 1, 1) = '+', '+', ''), CAST(`turnaround` AS SIGNED) + " + $adjust_by + ") WHERE 1";

【讨论】:

  • 很好,会的。您能否对这到底在做什么提供一点解释?我的 SQL 知识有点有限,我并不完全理解它。谢谢。
  • @Neodan WHERE 1?你可以忽略它,因为它总是正确的
  • 这是从方程式中删除 PHP 或任何其他语言的正确答案。它将文本列中的值转换为整数,将其递增并保存。对于糟糕的数据库模式,它基本上是一个很好的修复。来自我的 +1!
  • 感谢@Neodan 的解释,我正要问WHERE 1。这个因素如何影响它?这是指IF(SUBSTR)...是真的吗?
  • @MP_Webby no, WHERE 1 只是意味着 MySQL 必须从表中获取所有记录。这是稍微提高性能的老把戏,因为如果您的查询没有 WHERE 部分,那么 MySQL 会自行添加它。
【解决方案2】:

你不能直接说

UPDATE whatevertable SET whatever = whatever + 1?

试试看,我很确定它会起作用!

编辑:你有字符串或整数?您的数据库设计有缺陷,这可能行不通,但如果您的数据库设计更严格,这将是正确的答案。

【讨论】:

  • 是的,我知道,如果数据库和代码库设计得当,这将是完美的解决方案。可悲的是,这是我继承的。到处都是主要的设计问题 = 严重的头痛......
  • “继承”意味着您需要在继续之前进行清理。它不应该意味着你被蹩脚的设计所困扰。
【解决方案3】:

您可能没有,但需要这个“复合”索引(按任意顺序):

INDEX(optionid, turnaround)

请提供SHOW CREATE TABLE

另一个轻微的性能提升是在更新循环之前显式地LOCK TABLE WRITE。然后UNLOCK。注意:这仅适用于 MyISAM。

使用 InnoDB 会更好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-31
    • 2012-07-27
    • 2016-09-05
    • 1970-01-01
    • 2014-03-20
    • 2014-07-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多