【问题标题】:Handling large result set from mysql with limited memory使用有限的内存处理来自 mysql 的大型结果集
【发布时间】:2014-07-11 09:17:50
【问题描述】:

我有一个包含 1500 个人的实验结果的大型数据库。每个人有 96 个数据点。我编写了以下脚本来总结并格式化数据,以便分析软件可以使用它。起初一切都很好,直到我有超过 500 个人。现在我的内存不足了。

我想知道现在是否有人建议在不牺牲速度的情况下克服内存限制问题。

这就是表格在数据库中的样子

fishId assayId allele1 allele2

14_1_1 1 个 A T

14_1_1 2 A A

$mysql = new PDO('mysql:host=localhost; dbname=aquatech_DB', $db_user, $db_pass);
$query = $mysql->prepare("SELECT genotyped.fishid, genotyped.assayid, genotyped.allele1, genotyped.allele2, fishId.sex, " .
"fishId.role FROM `fishId` INNER JOIN genotyped ON genotyped.fishid=fishId.catId WHERE fishId.projectid=:project");
$query->bindParam(':project', $project, PDO::PARAM_INT);
$query->execute();  

所以这是对数据库的调用。它正在连接来自两个表的信息来构建我需要的文件。

 if(!$query){
    $error = $query->errorInfo();
    print_r($error);
} else { 
    $data = array();
    $rows = array();
    if($results = $query->fetchAll()){
        foreach($results as $row)
        {
            $rows[] = $row[0];
            $role[$row[0]] = $row[5];
            $data[$row[0]][$row[1]]['alelleY'] = $row[2];
            $data[$row[0]][$row[1]]['alelleX'] = $row[3];
        }
        $rows = array_unique($rows);
        foreach($rows as $ids)
        {
            $col2 = $role[$ids];
            $alelleX = $alelleY = $content = "";
            foreach($snp as $loci)
            {
                $alelleY = convertAllele($data[$ids][$loci]['alelleY']);
                $alelleX = convertAllele($data[$ids][$loci]['alelleX']);
                $content .= "$alelleY\t$alelleX\t";
            }
            $body .= "$ids\t$col2\t" . substr($content, 0, -1) . "\n";

这会解析数据。在我需要的文件中,我必须每个人有一行而不是每个人 96 行,这就是必须格式化数据的原因。在脚本的最后,我只是将 $body 写入文件。

我需要输出文件是

FishId 检测 1 检测 2

14_1_1 A T A A

$location = "results/" . "$filename" . "_result.txt";
$fh = fopen("$location", 'w') or die ("Could not create destination file");
if(fwrite($fh, $body))

【问题讨论】:

  • 不要使用$results = $query->fetchAll()){,而是一次获取和处理一行,因此您可以减少内存中 $results 的大小
  • 我发现,当您不使用 EVERY 操作写入文件时,可以获得最佳性能。根据数据的长度,我经常每 100-1000 行执行一次写操作。在您的情况下,如果内存紧张或行很长,那么每次写入操作可能读取 100 条记录? (与正常读取数据相比,触摸文件相当冗长)。
  • 谢谢,马克。我会试试的。
  • @anotherlife 。 . .您也许可以在数据库中做更多的工作。您应该使用输入数据和所需的输出来编辑您的问题。
  • 嗨 Fluffeh,我想你的意思是在 mysql 调用中使用 LIMIT,对吗?所以我可以用 SELECT COUNT 检查有多少行,然后每 500 行执行一次写操作?

标签: php mysql performance memory pdo


【解决方案1】:
  1. fetchAll() 一次性获取整个结果,这有其用途,但对内存很贪心。为什么不只使用一次处理一行的fetch()

  2. 您似乎是按第一列索引行,创建另一个大数组,然后删除重复项。为什么不在查询中使用SELECT DISTINCT 将重复项在它们到达 PHP 之前删除?

我不确定这会对速度产生什么影响 - fetch() 可能比 fetchAll() 慢 - 但您不必从数组中删除重复项,这样可以节省一些处理时间。

我也不确定您的第二个 foreach 正在做什么,但您应该能够一次性完成所有操作。 IE。提取循环中的foreach 循环。

对上述代码的其他观察:

  • $role 数组似乎与$rows 执行相同的索引工作 - 使用$row[0] 作为键有效地一次性删除重复项。通过SELECT DISTINCT 删除重复项可能更好,但如果不需要,您是否需要$rows 数组和array_unique 函数?
  • 如果$row[0] 的相同值可以有$row[5] 的不同值,那么您的索引方法将丢弃数据 - 但您知道数据中的内容,所以我猜您已经想到了(同样的可能是$data 数组为真)

【讨论】:

  • 嗨,史蒂夫,我无法选择 DISTINCT,因为由 fishId 索引的每一行都包含所有个人的 96 个数据点之一。所以我需要它们,但在输出文件中我需要将所有这 96 个点排成一行,这就是我使用 array_unique 的原因。因此,我可以获得个人列表,然后将每个人的 96 个数据点显示在一行中,而不是显示为 96 行。我希望这是有道理的。谢谢你的回答。
  • 在这种情况下,fetch 比 fetchAll 占用更少的内存。
  • 当您浏览行时,将它们添加到由单个 id 和数据点索引的数组中,例如 $array[$individualid][$datapoint]
【解决方案2】:

不要使用 fetchAll() 将数据库查询的整个结果读取到变量中,而是逐行获取它:

while($row = $query->fetch()) { ... }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-07
    • 2011-10-17
    • 2010-12-04
    • 1970-01-01
    • 1970-01-01
    • 2014-08-09
    相关资源
    最近更新 更多