BigQuery PHP API-大查询结果内存膨胀-即使使用分页

时间:2018-09-07 14:54:48

标签: php google-bigquery

我正在BigQuery中运行一系列查询,并将其通过PHP导出到CSV。对于我来说,这是最简单的方法,这有很多原因(多个查询取决于应用程序中的变量)。

当结果集大于100mb时,我正遇到内存问题。看来我的代码的内存使用量似乎与结果集一致,我认为分页可以避免这种情况。这是我的代码:

$query = $bq->query($myQuery);
$queryResults = $bq->runQuery($query,['maxResults'=>5000]);

$FH = fopen($storagepath, 'w');

$rows = $queryResults->rows();

foreach ($rows as $row) {
    fputcsv($FH, $row);
}

fclose($FH);

$queryResults->rows()函数返回一个Google Iterator,它使用分页来滚动结果,因此我不明白为什么脚本运行时内存使用量会增加。

我在浏览结果时是否缺少一种从内存中丢弃前一页的方法?

更新

我已经注意到,实际上,自从升级到v1.4.3 BigQuery PHP API以来,此过程的内存使用量确实达到了120mb,即使结果集远远超出了此范围(当前正在处理1gb结果集)。但是,120mb似乎太多了。如何识别和修复此内存的使用位置?

更新2 该120mb似乎在页面中的每个maxResult上以24kb的速率被捆绑。例如。向maxResults添加1000行会增加24mb的内存。所以现在我的问题是为什么1行数据在Google Iterator中使用24kb?有办法减少这种情况吗?数据本身为每行<1kb。

1 个答案:

答案 0 :(得分:1)

回答我自己的问题

PHP类型映射和BigQuery数据附带的其他数据结构信息会占用额外的内存。不幸的是,我找不到一种将内存使用量减少到每行大约24kb乘以页面大小以下的方法。 如果有人找到减少数据附带的膨胀的方法,请在下面发布

但是,由于有了其中一项评论,我意识到您可以将查询直接提取到Google Cloud Storage Bucket中的CSV。这真的很简单:

query = $bq->query($myQuery);

$queryResults = $bq->runQuery($query);

$qJobInfo = $queryResults->job()->info();

$dataset = $bq->dataset($qJobInfo['configuration']['query']['destinationTable']['datasetId']);

$table = $dataset->table($qJobInfo['configuration']['query']['destinationTable']['tableId']);

$extractJob = $table->extract('gs://mybucket/'.$filename.'.csv');

$table->runJob($extractJob);

但是,由于结果集超过1gb,这仍然不能解决我的问题,因此我不得不通过添加通配符来使用数据分片功能。

$extractJob = $table->extract('gs://mybucket/'.$filename.'*.csv');

这在存储桶中创建了约100个碎片。这些需要使用gsutil compose <shard filenames> <final filename>进行重组。但是,gsutil一次只能组成32个文件。鉴于我将拥有可变数量的分片(通常大于32),因此我不得不编写一些代码来清理它们。

//Save above job as variable
$eJob = $table->runJob($extractJob);

$eJobInfo = $eJob->info();

//This bit of info from the job tells you how many shards were created
$eJobFiles = $eJobInfo['statistics']['extract']['destinationUriFileCounts'][0];

$composedFiles = 0; $composeLength = 0; $subfile = 0; $fileString = "";

while (($composedFiles < $eJobFiles) && ($eJobFiles>1)) {

    while (($composeLength < 32) && ($composedFiles < $eJobFiles)) {
        // gsutil creates shards with a 12 digit number after the filename, so build a string of 32 such filenames at a time                            
        $fileString .= "gs://bucket/$filename" . str_pad($composedFiles,12,"0",STR_PAD_LEFT) . ".csv ";

        $composedFiles++;

        $composeLength++;

    }

    $composeLength = 0;

    // Compose a batch of 32 into a subfile
    system("gsutil compose $fileString gs://bucket/".$filename."-".$subfile.".csv");

    $subfile++;

    $fileString="";
}

if ($eJobFiles > 1) {
    //Compose all the subfiles                        
    system('gsutil compose gs://bucket/'.$filename.'-* gs://fm-sparkbeyond/YouTube_1_0/' . $filepath . '.gz') ==$
}

请注意,为了使我的Apache用户可以访问gsutil,我必须允许该用户在Web根目录中创建一个.config目录。理想情况下,您将使用gsutil PHP库,但我不希望代码膨胀。

如果有更好的答案,请发布

  1. 有没有办法从BigQuery库中获得比每行24kb小的输出?

  2. 是否有更有效的方法来清除可变数量的碎片?