【问题标题】:PDO insert query slowing the site downPDO 插入查询减慢站点速度
【发布时间】:2012-03-26 23:10:58
【问题描述】:

在我的网站上,我编写了一个函数,可以显示我每天获得的唯一身份访问者数量和网页浏览量。

问题是有时插入查询需要很长时间,并且在 InnoDB 中没有 DELAYED INSERT 函数。

编辑: 它使用 InnoDB,我的意思是长加载时间约为 6 秒,而不是 0.1-0.5 秒。一旦我删除日志记录,网站就会快得多。

下面的 $b 数组包含浏览器信息,但根据 XHProf 的说法,它的 PDO 查询需要很长时间才能执行。

插入代码是这样的:

$values = array(
        'time' => time(),
        'ip' => $_SERVER['REMOTE_ADDR'],
        'page' => rtrim((isset($_GET['q']) ? $_GET['q'] : 'index'), '/'),
        'browser' => $b[11][0] . ' ' . $b[11][1],
        'os' => $uos,
        'referred' => (isset($_SERVER['HTTP_REFERER']) && !preg_match('|^' . Config::getValue('site', 'url') . '|', $_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''),
    );

    $this->table->insert($values);

以及插入功能代码:

public function insert($table, $data) {
    ksort($data);

    $fieldNames = implode('`, `', array_keys($data));
    $fieldValues = ':' . implode(', :', array_keys($data));

    $sth = $this->prepare('INSERT INTO ' . $table . '(`' . $fieldNames . '`) VALUES (' . $fieldValues . ');');

    foreach ($data as $key => $value) {
        $sth->bindValue(':' . $key, $value);
    }

    $sth->execute();
}

【问题讨论】:

  • 这个问题需要更多细节。什么插入查询? “永远”有多长?数据库架构是什么样的?
  • 请发布表定义。如果主键不是自增且表很大,那么插入时涉及到的随机 I/O 可能是导致速度变慢的原因
  • 我以为你想做DELAYED INSERT,但你没有。所以我会说这需要更长的时间,因为你不做DELAYED INSERT,对吧?
  • 使用 INNODB 时不能有 DELAYED INSERT。
  • 那是您的实际代码吗? $this->table->insert($values); 您从不提供 $data,但您将实际值指定为 $table 参数

标签: php mysql nginx pdo innodb


【解决方案1】:

一个小的优化。您进行绑定的方式可以摆脱 foreach 语句并直接传递数组以执行。 PDO 会将数组键映射到您在准备语句中创建的 :binding 名称。

$sth->execute($data);

也就是说,您应该查看 mysql 中的慢查询日志以了解发生了什么。即使您使用的是 Innodb,在某些情况下 Innodb 也会锁定表。这可能是插入有时需要很长时间的原因。您可以将慢查询阈值降低到 3 秒来捕获查询。日志会告诉您每个阶段花费了多少时间(即锁定)。

【讨论】:

    【解决方案2】:

    “在我的网站上,我编写了一个函数,可以向您显示每天有多少独立访问者以及我获得的网页浏览量。”

    您是否为每个页面请求执行实时查询?这很可能是其自身的性能问题。如果您每隔一小时左右查询这些数字并缓存该值,它不会杀死任何人。然后,后续页面视图可以使用缓存值而不是实时值。

    “问题是有时插入查询需要很长时间,而在 InnoDB 中没有 DELAYED INSERT 函数。”

    可能不是 INSERT 本身让您放慢了速度,而是您的 MySQL 实例一直忙于计算页面浏览量。一旦您开始提供缓存的页面视图数据,这个问题就有可能会消失。

    【讨论】:

    • 尤其是在一个或多个字段(不包括主字段)上使用键时。
    【解决方案3】:

    正如 deceze 所说,它可能需要更多细节。但我想它可能会卡在“Config::getValue('site', 'url')”中。顺便说一句,你的 mysql 是远程的吗?

    【讨论】:

      【解决方案4】:

      您使用prepare 而不是仅仅调用pdo -> exec() 有什么原因吗?

      四处搜索显示许多人将prepare 标记为比使用exec() 更慢且更密集,尤其是当您不期望返回任何值时(另请参阅Prepare 的文档 - 它指出正在设计以优化多次调用同一个查询)。

      你可以做这样的事情,而不是绑定你的价值观 -

      function insert($table, $data) {
          ksort($data);
      
          $fieldNames = implode("`, `", array_keys($data));
          $fieldValues = implode("', '", array_values($data));
      
          $this->exec("INSERT INTO $table (`{$fieldNames}`) VALUES ('{$fieldValues}')");
      }
      

      【讨论】:

      • 这是引入 SQL 注入向量的好方法。
      猜你喜欢
      • 2011-10-04
      • 2015-11-27
      • 2013-10-18
      • 2020-02-22
      • 2017-12-24
      • 2016-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多