【问题标题】:Writing to files is slow写入文件很慢
【发布时间】:2016-10-29 16:59:41
【问题描述】:

所以,我有这个包含 435,453 行数据的数据库。而且我有 271 家不同的公司需要从中获取特定信息,因此我目前正在尝试通过使用 MySQL 查询选择所有数据来聚合它,该查询在 1.28 秒内返回所有行,很好。

然后我想根据每个公司遍历它们并将它们写入文件(使用 fputcsv())。所以我首先使用 fopen() 271 次来创建文件,然后对于每个返回的行,我查看哪些公司将在该行中有数据并相应地写入文件。

现在,使用 memory_usage() 我可以看到在整个过程中内存使用量稳定在 6Mb。通过使用 microtime(),我正在计时我从数据库中读取的 while() 的每次迭代。

最终结果是每次迭代大约需要 0.00001 秒,因此对于数据库中的每一行,确定哪些公司应该拥有每一行并将其写入这些文件需要 0.00001 秒。

但是有些事情搞砸了,因为 100 分钟后,这个过程仍然没有完成,while() 每秒前进大约 100 行。如果我的数学是正确的,遍历 435,453 行,每行需要 0.00001 秒应该需要大约 4 秒。

这里是代码,或者是花了这么长时间的部分:

$q=mysql_query(bq($query)) or print mysql_error();
while ($r=mysql_fetch_assoc($q)){
    $nr++;
    $now = microtime(true);
    if (($nr % 100) == 0 && $argv[1] == "debug"){
        print "$nr: " . fsize(memory_get_usage()) . ", time: " . sprintf("%.5f", ($now - $savetime)) . "\n";
    }
    foreach ($xsps as $xsp => $sites){
        if (!in_array($r["citynet"], $sites)) continue 1;
        $data = format_fields($r, $xsp);
        $values = array_values($data);
        $keys = array_keys($data);
        $linefeed = "\n";
        # TXT
        if ($nr == 1) fwrite($text[$xsp], join("\t", $keys) . $linefeed);
        fputcsv($text[$xsp], $values, "\t");
        # CSV
        if ($nr == 1) fwrite($csv[$xsp], join(";", $keys) . $linefeed);
        fputcsv($csv[$xsp], $values, ";");
    }
    $savetime = microtime(true);
}

每 100 行打印一次的输出如下所示:

12600: 6 Mb, time: 0.00000
12700: 6 Mb, time: 0.00000
12800: 6 Mb, time: 0.00000
12900: 6 Mb, time: 0.00001
13000: 6 Mb, time: 0.00000
13100: 6 Mb, time: 0.00000
13200: 6 Mb, time: 0.00000

那么,很明显 - 我做错了什么?数字说没有,怎么可能需要这么长时间?

编辑 所以,显然我的计算方式是错误的,所以我将其编辑为:

while ($r=mysql_fetch_assoc($q)){
    $nr++;
    $start = microtime(true);
    if (($nr % 100) == 0 && $argv[1] == "debug"){
        print "$nr: " . fsize(memory_get_usage()) . ", time: " . sprintf("%.5f", $processtime) . "\n";
    }
    ...
    $stop = microtime(true);
    $processtime = ($stop - $start);
}

现在它报告每行需要 0.15 秒才能完成,这意味着整个过程需要 108 分钟。所以现在的实际问题是——为什么这么慢?是 fputcsv() 慢还是 PHP 慢?

【问题讨论】:

  • 时间看起来不正确。在 foreach 循环之后,您存储时间,然后返回到 while 循环的开头并再次获取时间。 $now 几乎在 $savetime 之后立即计算。我认为你应该在$nr % 100 == 0$savetime应该在开始时计算$savetime,而$now时间需要在foreach之后,然后计算差异。
  • @Ctc 不,这是从命令行运行的
  • @sandman 我的错,没有真正正确地阅读它。让我再读一遍。
  • 尝试将结果/行存储在临时变量中,并在循环结束时或偶尔($nr % 100 或 1000)将它们转储到文件中,然后查看执行时间如何变化。在大多数情况下,写操作是瓶颈。
  • stop using mysql_* functionsThese extensions 已在 PHP 7 中删除。了解PDOMySQLiprepared 语句并考虑使用 PDO,it's really pretty easy

标签: php mysql csv fopen


【解决方案1】:

我无法调试您的代码和数据库。

但我将从更改此已弃用的mysql_* 代码开始:

$q=mysql_query(bq($query)) or print mysql_error();
while ($r=mysql_fetch_assoc($q)){

到这个 PDO 版本,例如:

$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
$user = 'dbuser';
$password = 'dbpass';

try {
    $dbh = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}

$sth = $dbh->prepare($q);
$sth->execute();

$result = $sth->fetchAll(PDO::FETCH_ASSOC); // <-- this is important to get all records from mysql but not one by one
foreach($result  as $r) {

【讨论】:

  • 我正在这样做,但那是另一回事,MySQL 连接和查询不是这里需要时间的。
  • 实际上是一个一个地获取元素,立即处理并忘记结果数组,内存效率更高。
  • 将 435,453 条记录输入内存似乎是个坏主意。也许将您的示例更改为使用简单的while($row = $sth-&gt;fetch(PDO::FETCH_ASSOC)) { ... }
  • 我不认为将 435,453 行数据提取到内存中是可行的方法。我怀疑更改为 PDO 会大大提高性能。不过更新到 PDO 会很好。
猜你喜欢
  • 2011-12-12
  • 2020-09-15
  • 2019-05-07
  • 1970-01-01
  • 2016-08-21
  • 1970-01-01
  • 2020-05-25
  • 2013-05-11
相关资源
最近更新 更多