【问题标题】:Query with in a loop getting slower and slower循环中的查询越来越慢
【发布时间】:2011-01-28 21:57:15
【问题描述】:

我有一个进程逐行读取数据馈送,解析数据并将其插入到 MyISAM 表中。当它第一次启动时,它运行得非常快,可能每秒大约 1000 条记录。随着时间的推移,它变得越来越慢,现在我们大约每 180 秒 1 行。

函数的一般语法是:

function parse($file) {
  $handle = fopen($file, 'r');
  while (!feof($handle)) {
    $line = fgets($fileHandle, 1000);
    switch (substr($line, 0, 2)) { //gets record type
      case '01' :
        //parse the record
        //escapes some strings with mysql_real_escape_string()
        mysql_query('INSERT INTO table VALUES ($a, $b, $c...');
      case '02' :
        ...
    }
  }
}

当前正在解析的文件有几百万条记录。服务器似乎没有丢失内存空间。有谁知道是什么导致进程变慢?

【问题讨论】:

  • 桌子上有索引吗?索引更新可能是罪魁祸首。另外,您多久提交一次?
  • 你在'table'上有索引吗?
  • @bensiu & @Jim Garrison - 为什么需要为 INSERT 编制索引?
  • 也许你应该发布使用你的架构?
  • @FeRtoll - INSERTS 不需要索引,但如果在执行 INSERT 时表上有索引,则必须重写索引以包含最近插入的记录 - 这将减慢插入过程

标签: php mysql performance


【解决方案1】:

这可能与必须如此频繁地写入索引有关。您已经在使用 MyISAM,这将是我的第一个建议。

一些建议

  1. 每 100 行批量插入
  2. 使用 INSERT DELAYED 可以让 MySQL 允许插入快速完成排队,当资源允许时 MySQL 会插入记录。
  3. 让您的脚本创建一个 SQL 转储/分隔文件,您可以使用 IN FILE 导入函数加载该文件,请参阅 http://dev.mysql.com/doc/refman/5.1/en/load-data.html,这将比批量插入快得多

【讨论】:

  • 谢谢,我会试试这些。只有一个索引,一个自增列上的主键。
  • @ParrisVarney - 您提供的 INSERT 与拥有 AUTO_INCREMENT 不兼容!请提供SHOW CREATE TABLE
【解决方案2】:

您可能在此表上至少有一个索引。所以每一行都被插入到一个越来越大的索引中。在开始加载之前,请执行

ALTER TABLE 表禁用键

完成后做

ALTER TABLE 表启用键

重新启用密钥可能需要一段时间。

在加载表格时不要尝试使用它。此外,插入延迟可能会有所帮助。但如果您在其他安静的数据库服务器上执行此表加载,则可能不会。

【讨论】:

    【解决方案3】:

    我认为你应该在事务中运行查询(它得到了加速),最好使用PDO,因为它更安全。 PDO 中的预处理语句可能会更快,但至少更安全,因为它们不受 SQL 注入的影响。这样会快很多。我为您准备了一个带有标签的示例:

    <?php
    
    $array = array(
        "ActionScript",
        "AppleScript",
        "Asp",
        "BASIC",
        "C",
        "C++",
        "Clojure",
        "COBOL",
        "ColdFusion",
        "Erlang",
        "Fortran",
        "Groovy",
        "Haskell",
        "Java",
        "JavaScript",
        "Lisp",
        "Perl",
        "PHP",
        "Python",
        "Ruby",
        "Scala",
        "Scheme"
    );
    
    function createTable($db) {
        $db->exec("CREATE TABLE IF NOT EXISTS tags (id INTEGER PRIMARY KEY, tag TEXT NOT NULL UNIQUE)");
    }
    
    function insertData($db, $array) {
        $db->beginTransaction();
    
        foreach($array as $elm) {
            try {
                $placeholder = array($elm);
                $stmt = $db->prepare("INSERT INTO tags (tag) VALUES (?)");
                $stmt->execute($placeholder);
            } catch(PDOException $e) {
                /*** roll back the transaction if we fail ***/
                $db->rollback();
                /*** echo the sql statement and error message ***/
                echo $sql . '<br />' . $e->getMessage();
            }
        }
    
        $db->commit();
    }
    
    $db = new PDO('sqlite:database/tags.sqlite3');
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
    //
    createTable($db);
    insertData($db, $array);
    

    P.S:我还想指出InnoDB 可能会更好。

    【讨论】:

    • MyISAM 完全忽略事务。
    • 数据来自内部提要,我不担心 SQL 注入。我认为我使用的 Php 版本不支持 PDO,但它可以为我的机箱添加弹药以进行升级。它是否做了 MySQL 尚未做的任何优化?
    • 哇,你用的是PHP5.1之前的PHP版本?你应该告诉你的管理员你需要 PHP5.3,因为你甚至不需要做任何事情就应该得到一个speed improvement of up to 30%。准备好的语句是预编译的,因此您应该/可以看到速度提高。
    • @PMV 我认为这是一件非常聪明的事情:P。
    • 并且mysql_*接口已经在最新版本中被移除了!
    最近更新 更多