2016-01-14 3 views
0

У этой проблемы возникла проблема с Kalev Leetaru GDELT: следующий запрос будет запущен в BigQuery при анализе полного месяца, но он не будет работать при переходе через целый год.Исправление «Ресурсов превышено» в BigQuery и ускорение его работы

SELECT Source, Target, count, RATIO_TO_REPORT(count) OVER() Weight 
FROM (
    SELECT a.name Source, b.name Target, COUNT(*) AS COUNT 
    FROM (FLATTEN(
     SELECT 
     GKGRECORDID, CONCAT(STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3))) AS name 
     FROM [gdelt-bq:gdeltv2.gkg] 
     WHERE DATE>20150100000000 and DATE<20151299999999, name)) a 
    JOIN EACH (
    SELECT 
     GKGRECORDID, CONCAT(STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3))) AS name 
    FROM [gdelt-bq:gdeltv2.gkg] 
    WHERE DATE>20150100000000 and DATE<20151299999999) b 
    ON a.GKGRECORDID=b.GKGRECORDID 
    WHERE a.name<b.name 
    AND a.name != '0.000000#0.000000' 
    AND b.name != '0.000000#0.000000' 
    GROUP EACH BY 1, 2 
    ORDER BY 3 DESC) 
WHERE count > 50 
LIMIT 500000 

«Ресурсы превышены во время выполнения запроса».

Как мы можем исправить это?

ответ

1

Сначала обратите внимание на оптимизацию затрат: взимается плата BigQuery за один столбец, и этот запрос будет проходить через 72GB. Таблица gg GDELT хранит всю историю в одной таблице - мы можем оптимизировать затраты, создавая годовые таблицы вместо одного.

Теперь, как мы можем исправить этот запрос, чтобы он пробежал целый год? «Ресурсы, превышаемые во время выполнения запроса», обычно исходят из немасштабируемых функций. Например:

  • RATIO_TO_REPORT(COUNT) OVER() не будет масштабироваться: OVER() функция запуска по всему набору результатов, что позволяет нам вычислить итоги и сколько из общего числа в каждой строке способствует - но для этого бежать нам нужно полный набор результатов, чтобы он входил в одну виртуальную машину. Хорошей новостью является то, что OVER() может масштабироваться при разделении данных, например, имея OVER (PARTITION BY month) - тогда нам понадобится только каждый раздел для размещения в виртуальной машине. Для этого запроса мы просто удалим этот столбец результатов для простоты.

  • ORDER BY не будет масштабироваться: для сортировки результатов нам нужны все результаты, чтобы поместиться на одной виртуальной машине тоже. Вот почему «-allow-large-results» не позволит запустить шаг ORDER BY, так как каждая VM будет обрабатывать и выводить результаты параллельно.

В этом запросе мы имеем простой способ справиться с ORDER BY масштабируемость - мы будем двигаться позже фильтр «WHERE COUNT> 50» ранее в этот процесс. Вместо того чтобы сортировать все результаты и фильтрации те, которые имели COUNT> 50, мы будем двигаться и изменить его к HAVING, так он работает до ORDER BY:

SELECT Source, Target, count 
FROM (
    SELECT a.name Source, b.name Target, COUNT(*) AS COUNT 
    FROM (FLATTEN(
     SELECT 
     GKGRECORDID, CONCAT(STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3))) AS name 
     FROM [gdelt-bq:gdeltv2.gkg] 
     WHERE DATE>20150100000000 and DATE<20151299999999,name)) a 
    JOIN EACH (
    SELECT 
     GKGRECORDID, CONCAT(STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3))) AS name 
    FROM [gdelt-bq:gdeltv2.gkg] 
    WHERE DATE>20150100000000 and DATE<20151299999999) b 
    ON a.GKGRECORDID=b.GKGRECORDID 
    WHERE a.name<b.name 
    AND a.name != '0.000000#0.000000' 
    AND b.name != '0.000000#0.000000' 
    GROUP EACH BY 1, 2 
    HAVING count>50 
    ORDER BY 3 DESC) 
LIMIT 500000 

А теперь запрос пробегает полный год данных!

Давайте посмотрим на статистику объяснения:

explanation stats for the first fixed query

Мы можем видеть, что таблица 188000000 строка была прочитана дважды: первый подзапрос произвел 1,5 млрд строк (учитывая «Свести»), и второй отфильтровал строки не в 2015 году (обратите внимание, что эта таблица начала хранить данные в начале 2015 года).

Этап 3 интересен: объединение обоих подзапросов позволило создать 3 миллиарда строк! Те получили уменьшенный до 500 миллионов с фильтром и агрегатные шагов:

enter image description here

Можем ли мы сделать лучше?

Да! Давайте перейдем к 2 WHERE a.name != '....' на более раннюю «ИМЕТЬ»:

SELECT Source, Target, count 
FROM (
    SELECT a.name Source, b.name Target, COUNT(*) AS COUNT 
    FROM (FLATTEN(
     SELECT 
     GKGRECORDID, CONCAT(STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3))) AS name 
     FROM [gdelt-bq:gdeltv2.gkg] 
     WHERE DATE>20150100000000 and DATE<20151299999999 
     HAVING name != '0.000000#0.000000',name)) a 
    JOIN EACH (
    SELECT 
     GKGRECORDID, CONCAT(STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3)), '#', STRING(ROUND(FLOAT(IFNULL(REGEXP_EXTRACT(SPLIT(V2Locations,';'),r'^[2-5]#.*?#.*?#.*?#.*?#.*?#(.*?)#'), '0')), 3))) AS name 
    FROM [gdelt-bq:gdeltv2.gkg] 
    WHERE DATE>20150100000000 and DATE<20151299999999 
    HAVING name != '0.000000#0.000000') b 
    ON a.GKGRECORDID=b.GKGRECORDID 
    WHERE a.name<b.name 
    GROUP EACH BY 1, 2 
    HAVING count>50 
    ORDER BY 3 DESC) 
LIMIT 500000 

Это работает еще быстрее!

Давайте посмотрим на статистику объяснения:

enter image description here

См? Перемещая фильтрацию на шаг до присоединения, этап 3 должен пройти только 1 миллиард строк, а не 3 миллиарда строк. Гораздо быстрее (даже для BigQuery, который, как вы можете проверить самостоятельно, способен в течение короткого промежутка времени собирать более 3 миллиардов строк, созданных JOIN).

И для чего был этот запрос?

Посмотрите на прекрасные результаты здесь: http://blog.gdeltproject.org/a-city-level-network-diagram-of-2015-in-one-line-of-sql/

enter image description here

+0

делает 'ORDER BY' внутри подзапроса гарантий соответствующий заказ на внешний/окончательный ВЫБРАТЬ? –

Смежные вопросы