【问题标题】:Google BigQuery queries are slowGoogle BigQuery 查询速度很慢
【发布时间】:2023-03-13 11:51:02
【问题描述】:

我正在使用 Google BigQuery,并且正在从 PHP 执行一些简单的查询。 (例如 SELECT * from emails WHERE email='mail@test.com')我只是检查表中是否存在电子邮件。

“电子邮件”表目前为空。但是 PHP 脚本仍然需要大约 4 分钟来检查一个空表上的 175 封电子邮件。我希望将来该表将被填满并且将有 500 000 封邮件,那么我猜请求时间会更长。

这正常吗?或者有什么改善检查时间的想法/解决方案?

(P.S.:“emails”表只有8列,都是字符串类型)

谢谢!

【问题讨论】:

  • 不建议对大查询进行 1 接 1 调用。您每天都会非常快地点击查询。您应该在同一查询中验证多个电子邮件时进行批量查询。
  • 谢谢,是的,它更快!只有 7.6 秒的执行时间。我想如果我想做一个 INSERT 查询也是一样的吗?但我不知道如何使用 PHP 对数千个插入执行单个插入请求?
  • 谢谢。我知道这存在,但我真的不知道如何在 PHP 中进行流式插入。在网上找不到任何示例。请问有示例吗?
  • 用 PHP 示例添加了答案。

标签: php sql google-app-engine bigdata google-bigquery


【解决方案1】:

如果您只是检查字段是否存在,请考虑改用SELECT COUNT(*) FROM emails where email='mail@test.com'。这只需要读取一个字段,因此成本更低,并且在大型表上速度略快。

正如 Pentium10 建议的那样,考虑在单个查询中使用多个查找。你可以这样做:

SELECT SUM((IF(email = 'mail1@test.com', 1, 0)) as m1,
       SUM((IF(email = 'mail2@test.com', 1, 0)) as m2,
       SUM((IF(email = 'mail3@test.com', 1, 0)) as m3,
       ...
 FROM emails

在单个查询中您将被限制为 64k 之类的数据,但计算速度应该非常快,因为它只需要一次扫描单个列。

或者,如果您希望每行一封电子邮件,您可以做一些更花哨的事情,比如

 SELECT email FROM emails WHERE email IN
 ('mail1@test.com', 'mail2@test.com', 'mail3@test.com'...)
 GROUP BY email

作为进一步的优化,您可以将其作为 LEFT JOIN:

SELECT t1.email as email, IF(t2.email is not null, true, false) as found 
FROM [interesting_emails] t1  
LEFT OUTER JOIN [emails] t2 ON t1.email = t2.email

如果interesting_emails 有您要查看的电子邮件列表,例如

mail1@test.com
mail2@test.com
mail3@test.com

如果 emails 表只包含 mail1@ 和 mail2@,那么你会得到结果:

email            found
______________   _____
mail1@test.com   true
mail2@test.com   false
mail3@test.com   true

这样做的好处是,如果需要,它可以扩展到数十亿封电子邮件(当数量变大时,您可能会考虑使用 JOIN EACH 而不是 JOIN)。

【讨论】:

  • 非常感谢您提供如此完整的带有示例的 cmets。它对我帮助很大,我将使用“IN”运算符进行检查。
  • 查询是否有任何限制,例如:SELECT email FROM emails WHERE email IN ('mail1@test.com', 'mail2@test.com', 'mail3@test.com'.. .) GROUP BY email 因为我尝试直接从 Google Big Query 界面检查 1200 左右,但它不起作用。我有:在第 1 行第 68098 列遇到“”=“=”。期待:
【解决方案2】:

这是一个用PHP做流式插入的示例代码,使用官方https://github.com/google/google-api-php-client

/**
 * 
 * @param type $client
 * @param type $project_id
 * @param type $dataset_id
 * @param type $rows
 * @return boolean
 * @throws Google_Service_Exception
 */
public function BQ_Tabledata_InsertAll($client, $project_id, $dataset_id, $rows) {
    $bq = new Google_Service_Bigquery($client);
    $request = new Google_Service_Bigquery_TableDataInsertAllRequest();
    $request->setRows($rows);
    try {
        $resp = new Google_Service_Bigquery_TableDataInsertAllResponse();
        $resp = $bq->tabledata->insertAll($project_id, $dataset_id, static::tableId(), $request);
        $errors = new Google_Service_Bigquery_TableDataInsertAllResponseInsertErrors();
        $errors = @$resp->getInsertErrors();
        if (!empty($errors)) {
            $error_msg = '';
            if (is_array($errors)) {
                $line = 0;
                foreach ($errors as $eP) {
                    $arr = $eP->getErrors();
                    if (is_array($arr)) {
                        foreach ($arr as $e) {
                            switch ($e->getReason()) {
                                case "stopped":
                                    break;
                                default:
                                    $error_msg.= sprintf("Error on line %s: %s\r\n", $line, $e->getMessage());
                                    break;
                            }
                        }
                    }
                    $line++;
                }
                $this->setErrorMessage($error_msg);
            } else {
                $this->setErrorMessage($errors);
            }
            //print_r($errors);
            //exit;
            return false;
        }
        return true;
    } catch (Google_Service_Exception $e) {
        $this->setErrors($e->getErrors())->setErrorMessage($e->getMessage());
        throw $e;
    }
}

【讨论】:

  • 非常感谢它正在工作,但我仍然只能在一个查询中插入 80 行。相反,我想插入 1200 左右。这可能吗?我收到此错误: [... ["modelData":protected]=> array(1) { ["insertErrors"]=> array(1148) ...["reason"]=> string(7) "stopped " ]
  • 在这里查看限制cloud.google.com/bigquery/streaming-data-into-bigquery,尤其是每个帖子的 1MB。
  • 是的。所以我每个请求执行 5 次,只是为了看看它做了什么。我有所有插入,但最后我有 5 个插入错误,如下所示:[0] => 数组([原因] => 无效 [消息] => 无法将值转换为字符串)
  • 这是一个数据类型错误。您需要检查并为每一列发送您在架构中设置的数据类型。看起来对于您定义为string 的列,您是其他东西而不是字符串。
  • 它实际上只有一行 int 类型,我不知道它是怎么发生的。然而,检查我们想要插入的数据总是好的。谢谢你的回答:)
猜你喜欢
  • 1970-01-01
  • 2016-06-08
  • 1970-01-01
  • 2019-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多