Google BigQuery запросы медленные

Я использую Google BigQuery и выполняю несколько простых запросов из PHP. (например, ВЫБРАТЬ * из электронных писем, ГДЕ email=’mail@test.com ‘) Я просто проверяю, существует ли электронная почта в таблице.

Таблица «электронные письма» пока пуста. Но все же PHP-скрипт занимает около 4 минут, чтобы проверить 175 электронных писем на пустой таблице. Как бы я хотел, чтобы в будущем таблица была заполнена и имела 500 000 писем, тогда, я думаю, время запроса будет больше.

Это нормально? Или есть какие-нибудь идеи / решения для улучшения времени проверки?

(П.С.: Таблица «электронные письма» содержит только 8 столбцов, все они строкового типа)

Спасибо !

2

Решение

Если вы просто проверяете наличие поля, подумайте об использовании 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

Вы будете ограничены чем-то вроде 64 тыс. Из них в одном запросе, но вычисление должно выполняться очень быстро, так как для этого требуется сканирование только одного столбца за один проход.

С другой стороны, если вы хотите, чтобы электронные письма были по одному на строку, вы могли бы сделать что-то более изобразительное, например:

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

В качестве дальнейшей оптимизации вы можете сделать это как левое соединение:

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

Если бы в поле Popular_emails был список писем, которые вы хотите проверить, например

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

Если таблица электронных писем содержит только mail1 @ и maiil2 @, вы получите результат в виде:

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

Преимущество такого способа состоит в том, что при необходимости он масштабируется до миллиардов электронных писем (когда число становится большим, вы можете рассмотреть возможность использования JOIN EACH вместо JOIN).

3

Другие решения

Вот пример кода в 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;
}
}
1