【问题标题】:BigQuery PHP API - large query result memory bloat - even with pagingBigQuery PHP API - 大型查询结果内存膨胀 - 即使有分页
【发布时间】:2018-09-09 06:54:22
【问题描述】:

我正在 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() 函数返回一个谷歌迭代器,它使用分页来滚动结果,所以我不明白为什么脚本运行时内存使用量会增加。

当我翻阅结果时,我是否错过了一种从内存中丢弃先前页面的方法?

更新

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

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

【问题讨论】:

  • 为什么不将结果以 CSV 格式导出到 Cloud Storage,然后复制文件?
  • 我看不到将查询直接导出到 CSV 的方法。我可以将结果保存到表中,然后导出表,但这需要我在运行查询时创建大量临时表,以及清理等。有没有办法将查询结果直接导出到 CSV?
  • 运行查询总是会创建一个临时表,您可以将其导出。
  • 请您指出如何执行此操作的方向吗?我在文档中找不到它。
  • cloud.google.com/bigquery/docs/exporting-data,另见cloud.google.com/bigquery/docs/reference/rest/v2/jobs 中的configuration.query.destinationTable 属性。它在运行查询后填充。

标签: php google-bigquery


【解决方案1】:

回答我自己的问题

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

但是,感谢其中一个 cmets,我意识到您可以将查询直接提取到 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. 有没有更有效的方法来清理可变数量的分片?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 2021-01-26
    • 2017-09-15
    • 2018-01-27
    • 2013-03-25
    相关资源
    最近更新 更多