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

BigQuery PHP API-大查询结果内存膨胀-即使有分页,php,google-bigquery,Php,Google Bigquery,我正在BigQuery中运行一系列查询,并通过PHP将它们导出到CSV。这是我做这件事最简单的方法,有很多原因(多个查询依赖于一个应用程序中的变量) 当结果集大于100mb时,我正在努力解决内存问题。代码的内存使用似乎随着结果集的增长而增长,我认为分页可以避免这种情况。这是我的密码: $query = $bq->query($myQuery); $queryResults = $bq->runQuery($query,['maxResults'=>5000]); $FH =

我正在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迭代器,该迭代器使用分页来滚动结果,因此我不理解为什么随着脚本的运行内存使用量会增加

当我翻阅结果时,是否丢失了从内存中丢弃前几页的方法

更新

我注意到,实际上,自从升级到v1.4.3 BigQuery PHP API之后,这个过程的内存使用量确实达到了120mb的上限,即使结果集远远超过这个上限(目前正在处理1gb的结果集)。不过,120mb似乎太多了。如何识别和修复此内存的使用位置

更新2
这120mb似乎与页面中每个
maxResult
的24kb绑定。例如,将1000行添加到
maxResults
会增加24mb的内存。所以我现在的问题是为什么一行数据在谷歌迭代器中使用24kb?有没有办法减少这种情况?数据本身每行<1kb。回答我自己的问题

额外的内存用于加载PHP类型映射和其他数据结构信息,这些信息与来自BigQuery的数据一起提供。不幸的是,我找不到一种方法将内存使用率降低到每行24kb乘以页面大小。如果有人想办法减少数据带来的膨胀,请在下面发布

不过,感谢其中一条评论,我意识到您可以将查询直接提取到谷歌云存储桶中的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
重新组合。但是,
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') ==$
}
//将上述作业另存为变量
$eJob=$table->runJob($extractJob);
$eJobInfo=$eJob->info();
//作业中的这些信息告诉您创建了多少个碎片
$eJobFiles=$eJobInfo['statistics']['extract']['destinationUriFileCounts'][0];
$composedFiles=0$复合长度=0$子文件=0$fileString=“”;
而(($composedFiles<$eJobFiles)&&($eJobFiles>1)){
而($composeLength<32)和&($composedFiles<$eJobFiles)){
//gsutil在文件名后创建一个12位数的碎片,因此一次构建一个由32个这样的文件名组成的字符串
$fileString.=“gs://bucket/$filename.str_-pad($composedFiles,12,“0”,str_-pad_-LEFT)。”.csv”;
$composedFiles++;
$composeLength++;
}
$composeLength=0;
//将一批32人组成一个子文件
系统(“gsutil compose$fileString gs://bucket/”$filename.-“$subfile..csv”);
$subfile++;
$fileString=“”;
}
如果($eJobFiles>1){
//组成所有子文件
系统('gsutil compose gs://bucket/'.$filename.-*gs://fm sparkbeyond/YouTube_1_0/'.$filepath..gz'))==$
}
注意:为了让我的Apache用户能够访问
gsutil
,我必须允许用户在web根目录中创建一个
.config
目录。理想情况下,您可以使用gsutil PHP库,但我不希望代码膨胀

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

  • 有没有办法从BigQuery库中获得小于每行24kb的输出

  • 有没有更有效的方法来清理数量可变的碎片


  • 为什么不将结果以CSV格式导出到云存储,然后复制文件?我找不到将查询直接导出到CSV的方法。我可以将结果保存到一个表中,然后导出该表,但这需要我在运行查询以及清理等时创建大量临时表。有没有办法将查询结果直接导出到CSV?运行查询始终会创建一个临时表,您可以导出该表。请告诉我如何执行此操作?我在文档中找不到它,请参见中的
    configuration.query.destinationTable
    属性。它是在运行查询后填充的。