【问题标题】:Drastically speed up 10 million row insert in MySQL database大幅加速 MySQL 数据库中的 1000 万行插入
【发布时间】:2014-04-12 14:22:28
【问题描述】:
$files = glob('dataset/*.xml');

foreach ($files as $key => $txc) {
    $txcDoc = new DOMDocument();
    $txcDoc->load($txc);

    $operators = $txcDoc->getElementsByTagName("Operators");
    foreach ($operators as $operatorTag) {
        foreach ($operatorTag->getElementsByTagName("Operator") as $operator) {
            $reference = $operator->getAttribute("id");
            @$nationalOperatorCode = $operator->getElementsByTagName("NationalOperatorCode")->item(0)->nodeValue;
            $operatorCode = $operator->getElementsByTagName("OperatorCode")->item(0)->nodeValue;
            $operatorShortName = $operator->getElementsByTagName("OperatorShortName")->item(0)->nodeValue;
            @$operatorNameOnLicense = $operator->getElementsByTagName("OperatorNameOnLicense")->item(0)->nodeValue;
            @$tradingName = $operator->getElementsByTagName("TradingName")->item(0)->nodeValue;

            $operatorSQL = "INSERT IGNORE INTO `operator` (`reference`, `national_operator_code`, `operator_code`, `operator_short_name`, `operator_name_on_license`, `trading_name`) VALUES (:reference, :nationalOperatorCode, :operatorCode, :operatorShortName, :operatorNameOnLicense, :tradingName);";

            $operatorStmt = $conn->prepare($operatorSQL);
            $operatorStmt->execute(array(':reference' => $reference, ':nationalOperatorCode' => $nationalOperatorCode, ':operatorCode' => $operatorCode, ':operatorShortName' => $operatorShortName, ':operatorNameOnLicense' => $operatorNameOnLicense, ':tradingName' => $tradingName));
        }
    }
}

上面的 PHP 循环遍历 78,654 个 XML 文件 (1.2gb),解析它们的数据,然后将数据插入 MySQL 数据库。上面的 sn-p 只占文件的十分之一,但是还有另外 10-15 个 foreach 构造,就像 foreach ($operators 一样。 (查看整个文件请点击here

我的问题是插入 250 个文件的数据需要 10-20 分钟。我需要大幅提高速度,以便在

数据库引擎是 MySQL,表都是 InnoDB。我怎样才能加快这些插入速度?

【问题讨论】:

    标签: php mysql insert innodb


    【解决方案1】:

    显然有很多东西要看,而你并没有提供太多细节......

    但是,通常可以轻松加速此类大规模插入的一件事是:

    1. 删除所有在插入数据的表上定义的索引

    2. 插入数据

    3. 重新创建之前定义的所有索引

    这加快了速度的原因是索引必须重新组织并且只写入一次一次,而不是每次插入操作。我经常惊讶于这有多大的不同......

    如果您真的想调整您的 php 实现,那么在示例运行中使用分析器来了解确切的时间花费在哪里是有意义的。专注于那些真正突出的部分。但请记住,将无尽的时间投入完美主义是没有意义的。拥有一个 CPU 工作比浪费你的时间便宜得多:-)

    【讨论】:

    • 感谢您的回答。有没有什么简单的方法可以重新添加所有索引?不同的表中大约有 10-20 个不同的索引,我可以在删除它们之前保存用于创建它们的 SQL,还是必须手动完成并重新添加它们?
    • 当然,只需将表定义转储到文件中(例如使用mysqldump)。在该转储中,您不仅会找到列定义,还会找到索引定义。完成插入后,您可以简单地复制和粘贴这些内容。
    • 另外一件事,所有这些foreach循环都是异步运行的,如果我一次只运行一个foreach循环会提高速度吗?
    • 不确定你的意思。 PHP 确实以异步方式执行此类循环。实际上 PHP 不知道这样的特性。这并没有真正的意义,因为循环内容可能会相互引用并且通常会这样做。这就是嵌套它们的全部意义所在。因此,每个外部循环仅在其内部循环完成所有次迭代时继续下一次迭代。严格按顺序处理。
    【解决方案2】:

    我认为你不必调用 $conn->prepare($operatorSQL);在每个循环中,您可以只在循环外准备语句并在循环内执行它。我不知道您实际上会获得多少性能改进,但是当您拥有数百万行时,以这种方式重构它是有意义的。

    此外,@ 运算符(又名静默运算符)会降低 PHP 脚本的速度。

    也许代码本身没问题,但如果您使用的是远程 sql 服务器,则可能是带宽问题。

    您可能会考虑根本不使用 php - 例如,Java 在解析大量数据时会快得多。使用 Java 时,您可以创建多线程程序,该程序可以同时解析多个文件。

    最后但同样重要的是 - 您可以升级硬件。读取数千个文件对于磁盘 I/O 来说会很繁重。使用 SSD 而不是 HDD 可以加快速度。

    就像其他人提到的那样,您实际上应该对您的代码进行基准测试。 Xdebug 是众所周知的 php 工具,可以生成 profiling 信息。根据分析信息,您可以在代码中找到bottlenecks,当您真正知道问题出在哪里时,您可以考虑前面提到的可能性。

    【讨论】:

      猜你喜欢
      • 2019-10-07
      • 1970-01-01
      • 2021-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多