【问题标题】:How to do multiple MySQL INSERTs in a way that scales (PHP)如何以可扩展的方式执行多个 MySQL INSERT (PHP)
【发布时间】:2011-01-10 08:36:24
【问题描述】:

我们的 PHP 代码中有一个循环,它将行插入到表中。例如:

while ($info = mysql_fetch_assoc($someQuery)) {
    mysql_query("INSERT INTO X (i,j) VALUES ($info['ii'],$info['jj'])");
}

几个月前这很好,因为循环只会迭代几次。然而,由于我们的网站获得更多流量,这个循环现在有时会迭代 1000 次或更多次。该表有一些 overhead (4,305 KiB) 并且来自该表的 SELECT 出现在 MySQL slow-log 中,可能是因为它们必须等待一长串 INSERT 才能释放锁?

我应该如何优化代码以便更好地扩展?

我认为我可以尝试的一些事情:

  • INSERT DELAYED - 需要调查一下。有帮助吗?
  • 尝试在同一个查询中插入多行。但是我应该设置什么限制? 50、500、1000?

【问题讨论】:

    标签: php mysql optimization scaling


    【解决方案1】:

    新版本的 MySQL 中有存储过程和函数。您可以在那里使用带有变量、循环和条件块的结构化语言。一个存储过程应该比许多独立的插入更快。但是,它是一种需要学习的新语言。

    你需要在php中扩展mysqli,才能使用存储过程。

    你也可以看看mysqli_multi_query。
    http://pl.php.net/manual/en/mysqli.multi-query.php

    【讨论】:

      【解决方案2】:

      将数据导入 MySQL 的最快方法之一是LOAD DATA INFILE。将其视为 CSV 导入。您可以一次将数据一行写入磁盘,然后进行批量加载。根据this page on insert speedsLOAD DATA INFILE 比直接INSERT 快20 倍。

      但是,可能还有其他不良副作用,因为表格很可能在整个过程中被锁定,而不是一次只锁定一行。在例如运行这个100 行批次可能会使问题的两个部分都易于管理 - 我认为您只需要进行试验。

      【讨论】:

        【解决方案3】:

        你也可以这样建立一个语句:

        INSERT INTO X (i,j)
        VALUES ($info['ii'],$info['jj']),
        (val, val),
        ...
        (val, val);
        

        您可以尝试不同的限制来确定您的 SQL 在哪里(如果在任何地方)变得过长。然后你可以将限制设置为理智的东西。

        【讨论】:

        • 我倾向于将其限制为 100-200 个值集,具体取决于每个部分的数据量。将其包装在“BEGIN / COMMIT”事务中也会有所帮助。
        【解决方案4】:

        本詹姆斯所说的很重要。如果您执行相同的 SQL,并且只需更改其中的参数,则准备好的语句会快得多。

        另外,我尝试将整个循环更改为:

        INSERT INTO x (i, j) SELECT (here goes your $someQuery)
        

        当然你必须修改 $someQuery,所以我只选择了两列与列 i 和 j 具有相同类型(或可以自动转换)。

        如果你在 php 中没有非常复杂的机制,那么这应该是可能的,并且会比任何 php 循环快得多。

        【讨论】:

          【解决方案5】:

          什么是$someQuery?你能用INSERT ... SELECT语法吗?

          【讨论】:

          • 感谢您的建议 - 我阅读了有关 INSERT...SELECT 的信息(通过您的链接),但不幸的是,我必须对数据进行一些处理(例如,addlashes)并插入一个没有'不是来自 SELECT。
          • 如果您在生成插入查询之前使用addslashes 转义字符串值,则此方法不需要这样做。如果您使用更多上下文更新您的问题(特别是 $someQuery 以及您对结果进行的任何处理),那么为您提供帮助会更容易。
          • 添加到问题中的代码有点冗长。此外,我并没有试图解决我的一个特定问题 - 我试图学习一般性以编写更好的代码 :-) 你已经提供了足够多的帮助,我很感激。
          • 好的,汤姆;我很高兴它对你有帮助。祝你好运:)
          【解决方案6】:

          不要重复mysql_query(),而是使用prepared statements。它们是使用不同值多次重复相同查询的更有效方式。

          另外,我会查看慢日志中出现的那些查询。使用EXPLAIN (<query>) 检查是否正在使用索引。

          【讨论】:

          • 感谢您的建议,但目前代码库不使用 PDO 甚至 mysqli。
          • 我可以看到,但这确实是开始使用更高级数据库模块的好案例。许多mysql_ 函数都有mysqli_ 等效项,因此您可能会发现切换到mysqli 很容易,然后只优化几个查询(例如上面的那个)作为准备好的语句。
          • 好的,谢谢 Ben - 我会接受这一点,并尝试与其他开发人员一起采取行动。
          猜你喜欢
          • 2010-09-24
          • 1970-01-01
          • 1970-01-01
          • 2011-12-22
          • 2012-01-18
          • 1970-01-01
          • 1970-01-01
          • 2018-05-19
          • 2016-07-13
          相关资源
          最近更新 更多