我正在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。
答案 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库,但我不希望代码膨胀。
如果有更好的答案,请发布
有没有办法从BigQuery库中获得比每行24kb小的输出?
是否有更有效的方法来清除可变数量的碎片?