Otimizar a computação em consultas
Este documento apresenta as práticas recomendadas para otimizar o desempenho da sua consulta.
Após a conclusão da consulta, é possível
ver o plano de consulta
no Console do Google Cloud. Também é possível solicitar detalhes da execução usando as
visualizações INFORMATION_SCHEMA.JOBS*
ou o
método jobs.get
da API REST.
O plano de consulta inclui detalhes sobre os estágios e as etapas da consulta. Esses detalhes podem ajudar a identificar formas de melhorar o desempenho da consulta. Por exemplo, se você perceber um estágio que grava muito mais resultados do que outros, pode ser necessário filtrar anteriormente na consulta.
Para saber mais sobre o plano de consulta e ver exemplos de como as informações dele podem ajudar a melhorar o desempenho da consulta, consulte Receber insights de desempenho da consulta. Depois de abordar os insights de desempenho da consulta, é possível otimizar ainda mais a consulta realizando as seguintes tarefas:
- Reduzir os dados que vão ser processados
- Otimizar operações de consulta
- Reduzir o resultado da sua consulta
- Usar uma reserva do BigQuery BI Engine
- Evitar padrões anti-SQL
- Especificar restrições no esquema da tabela
Reduzir dados processados
É possível reduzir os dados que precisam ser processados usando as opções descritas nas seções a seguir.
Evite SELECT *
Prática recomendada: controle a projeção consultando apenas as colunas necessárias.
Projeção se refere ao número de colunas lidas pela consulta. Projetar colunas em excesso incorre em E/S adicional (desperdiçada) e materialização (gravar resultados).
- Use as opções de visualização de dados. Se você estiver testando ou explorando dados, use uma das opções de visualização de dados em vez de
SELECT *
. - Consulte colunas específicas. A aplicação de uma cláusula
LIMIT
a uma consultaSELECT *
não afeta a quantidade de dados lidos. Você será cobrado pela leitura de todos os bytes na tabela inteira, e a consulta é contabilizada na sua cota de nível gratuito. Em vez disso, consulte apenas as colunas de que você precisa. Por exemplo, useSELECT * EXCEPT
para excluir uma ou mais colunas dos resultados. - Usar tabelas particionadas. Se você precisar fazer a consulta em todas as colunas de uma tabela, mas apenas em um subconjunto de dados, faça o seguinte:
- materializar os resultados em uma tabela de destino e realizar a consulta nessa tabela.
- Particionar suas tabelas
e consultar a partição relevante.
Por exemplo, use
WHERE _PARTITIONDATE="2017-01-01"
para consultar apenas a partição de 1 de janeiro de 2017.
Use
SELECT * EXCEPT
. Consultar um subconjunto de dados ou usarSELECT * EXCEPT
pode reduzir muito a quantidade de dados lidos por uma consulta. Além da economia de custos, o desempenho é melhorado reduzindo-se o montante de E/S de dados e de materialização obrigatória para os resultados da consulta.SELECT * EXCEPT (col1, col2, col5) FROM mydataset.newtable
Evite tabelas de caracteres curinga em excesso
Prática recomendada: ao consultar tabelas curinga, você precisa usar o prefixo mais granular.
Use caracteres curinga para consultar várias tabelas usando instruções SQL concisas. As tabelas de caracteres curinga são uma união de tabelas correspondente a uma expressão de caractere curinga. As tabelas curinga são úteis quando o conjunto de dados contém os seguintes recursos:
- várias tabelas nomeadas de maneira semelhante com esquemas compatíveis;
- Tabelas fragmentadas
Ao consultar uma tabela com caracteres curinga, especifique um caractere curinga (*
) após o prefixo da tabela
em comum. Por exemplo, FROM
consulta todas as tabelas dos anos 1940.bigquery-public-data.noaa_gsod.gsod194*
Os prefixos mais detalhados têm um desempenho melhor do que os mais curtos. Por exemplo, FROM
tem desempenho melhor do que bigquery-public-data.noaa_gsod.gsod194*
FROM
porque menos tabelas correspondem ao caractere curinga.bigquery-public-data.noaa_gsod.*
Evitar tabelas fragmentadas por data
Prática recomendada: não use tabelas fragmentadas por data (também chamadas de tabelas com nome de data) no lugar de tabelas particionadas por tempo.
Tabelas particionadas têm melhor desempenho do que as tabelas com nome de data. Quando você cria tabelas fragmentadas por data, o BigQuery precisa manter uma cópia do esquema e dos metadados para cada tabela com nome de data. Além disso, quando as tabelas com nome de data são usadas, pode ser necessário que o BigQuery verifique as permissões de cada tabela consultada. Essa prática também sobrecarrega a consulta e afeta o desempenho dela.
Evitar a fragmentação excessiva de tabelas
Prática recomendada: evite criar fragmentos de tabela em demasia. Se você estiver fragmentando tabelas por data, use tabelas particionadas por tempo.
A fragmentação de tabelas refere-se a dividir grandes conjuntos de dados em tabelas separadas e adicionar um sufixo a cada nome da tabela. Se você estiver fragmentando tabelas por data, use tabelas particionadas por tempo.
Devido ao baixo custo do armazenamento do BigQuery, você não precisa otimizar suas tabelas por causa do custo, como faria em um banco de dados relacional. Criar um grande número de fragmentos de tabela tem impactos de desempenho que superam os benefícios de custo.
As tabelas fragmentadas exigem que o BigQuery mantenha o esquema, os metadados e as permissões para cada fragmento. Devido à sobrecarga extra necessária para manter informações em cada fragmento, o desempenho da consulta poderá ser afetado se houver fragmentação em excesso.
A quantidade e a origem dos dados lidos por uma consulta podem afetar o desempenho e o custo dela.
Elimine consultas particionadas
Prática recomendada: ao consultar uma tabela particionada para filtrar por partições em tabelas particionadas, use as seguintes colunas:
- Para tabelas particionadas por tempo de processamento, use a pseudocoluna
_PARTITIONTIME
. - Para tabelas particionadas, como baseadas em coluna de tempo e intervalo de números inteiros, use a coluna de particionamento.
Para tabelas particionadas por unidade de tempo, filtrar os dados com _PARTITIONTIME
ou coluna de particionamento permite especificar uma data ou um intervalo de datas. Por exemplo, a cláusula WHERE
a seguir usa a pseudocoluna _PARTITIONTIME
para especificar partições entre 1º e 31 de janeiro de 2016:
WHERE _PARTITIONTIME
BETWEEN TIMESTAMP("20160101")
AND TIMESTAMP("20160131")
A consulta processa dados apenas nas partições indicadas pelo intervalo de datas. Filtrar suas partições melhora o desempenho da consulta e reduz os custos.
Reduzir os dados antes de usar um JOIN
Prática recomendada: reduza a quantidade de dados processados antes de uma cláusula JOIN
realizando agregações com antecedência.
O uso de uma cláusula GROUP BY
com funções agregadas
é computacionalmente caro, porque esses tipos de consultas usam
shuffle.
Como essas consultas usam muitos recursos computacionais, use uma cláusula GROUP BY
somente quando necessário.
Para consultas com GROUP BY
e JOIN
, execute a agregação anteriormente na consulta para reduzir a quantidade de dados processados.
Por exemplo, a consulta a seguir executa uma JOIN
em duas tabelas grandes sem nenhuma filtragem antes:
WITH users_posts AS ( SELECT * FROM `bigquery-public-data`.stackoverflow.comments AS c JOIN `bigquery-public-data`.stackoverflow.users AS u ON c.user_id = u.id ) SELECT user_id, ANY_VALUE(display_name) AS display_name, ANY_VALUE(reputation) AS reputation, COUNT(text) AS comments_count FROM users_posts GROUP BY user_id ORDER BY comments_count DESC LIMIT 20;
Esta consulta pré-agrega a contagem de comentários, o que reduz a quantidade de dados lidos para o JOIN
:
WITH comments AS ( SELECT user_id, COUNT(text) AS comments_count FROM `bigquery-public-data`.stackoverflow.comments WHERE user_id IS NOT NULL GROUP BY user_id ORDER BY comments_count DESC LIMIT 20 ) SELECT user_id, display_name, reputation, comments_count FROM comments JOIN `bigquery-public-data`.stackoverflow.users AS u ON user_id = u.id ORDER BY comments_count DESC;
Use a cláusula WHERE
Prática recomendada: use uma
cláusula WHERE
para limitar a quantidade de dados que uma consulta retorna. Quando possível, use as colunas BOOL
, INT
, FLOAT
ou DATE
na cláusula WHERE
.
As operações nas colunas BOOL
, INT
, FLOAT
e DATE
geralmente são mais rápidas
do que as operações nas colunas STRING
ou BYTE
. Quando possível, use uma coluna que
utilize um desses tipos de dados na cláusula WHERE
para reduzir a quantidade de
dados retornados pela consulta.
Otimizar operações de consulta
É possível otimizar suas operações de consulta usando as opções descritas nas seções a seguir.
Evitar transformar dados repetidamente
Prática recomendada: se você estiver usando o SQL para executar operações ETL, evite situações em que você está transformando repetidamente os mesmos dados.
Por exemplo, se estiver usando SQL para cortar strings ou extrair dados usando expressões regulares, terá mais desempenho para materializar os resultados transformados em uma tabela de destino. Funções como expressões regulares requerem cálculo adicional. Consultar a tabela de destino sem a sobrecarga adicional de transformação é muito mais eficiente.
Evitar várias avaliações dos mesmos CTEs
Prática recomendada: use linguagem procedural, variáveis, tabelas temporárias e tabelas que expiram automaticamente para manter os cálculos e usá-los posteriormente na consulta.
Quando a consulta contém expressões de tabela comuns (CTEs, na sigla em inglês) usadas em vários locais, elas podem ser avaliadas sempre que são referenciadas. O otimizador de consultas tenta detectar partes que poderiam ser executadas apenas uma vez, mas isso nem sempre é possível. Como resultado, usar uma CTE pode não ajudar a reduzir a complexidade da consulta interna e o consumo de recursos.
É possível armazenar o resultado de um CTE em uma variável escalar ou em uma tabela temporária, dependendo dos dados retornados pelo CTE.
Evite subconsultas e mesclagens repetidas
Prática recomendada: evite juntar repetidamente as mesmas tabelas e usar as mesmas subconsultas.
Em vez de juntar repetidamente os dados, pode ser mais eficiente usar dados repetidos aninhados para representar os relacionamentos. Os dados repetidos aninhados salvam o impacto de desempenho da largura de banda de comunicação exigida por uma junção. Isso também reduz os custos de E/S gerados pela leitura e gravação repetida dos mesmos dados. Para mais informações, consulte usar campos aninhados e repetidos.
Da mesma forma, repetir as mesmas subconsultas afeta o desempenho ao processar consultas repetidamente. Se você estiver usando as mesmas subconsultas em várias consultas, pense em materializar os resultados da subconsulta em uma tabela. Em seguida, use os dados materializados nas suas consultas.
A materialização dos resultados da subconsulta melhora o desempenho e reduz a quantidade geral dos dados que o BigQuery lê e grava. O pequeno custo de armazenar dados materializados supera o impacto no desempenho do processamento repetido de E/S e consultas.
Otimizar os padrões de união
Prática recomendada: para consultas que unem dados de várias tabelas, otimize os padrões de agrupamento começando pela tabela maior.
Ao criar uma consulta usando a cláusula JOIN
, considere a ordem em que você está mesclando os dados. O otimizador de consulta GoogleSQL determina qual tabela deve ficar em qual lado da junção. Como prática recomendada, coloque a tabela com
o maior número de linhas primeiro, seguida da tabela com o menor número de linhas e,
em seguida, as tabelas restantes diminuindo o tamanho.
Quando você tem uma tabela grande no lado esquerdo do JOIN
e uma pequena no lado direito do JOIN
, uma união de transmissão é criada. Uma união de transmissão envia todos os dados para a tabela menor para cada slot que processa a tabela maior. Recomenda-se realizar primeiro a união de transmissão.
Para ver o tamanho das tabelas no JOIN
, consulte
Acessar informações sobre tabelas.
Otimizar a cláusula ORDER BY
Prática recomendada: ao usar a cláusula ORDER BY
, siga
as práticas recomendadas:
Use
ORDER BY
na consulta mais externa ou em cláusulas de janela. Coloque operações complexas no final da consulta. Colocar uma cláusulaORDER BY
no meio de uma consulta afeta significativamente o desempenho, a menos que ela esteja sendo usada em uma função (analítica) de janela.Outra técnica para ordenar a consulta é colocar operações complexas no final da consulta, como expressões regulares e funções matemáticas. Essa técnica reduz os dados a serem processados antes que operações complexas sejam realizadas.
Use uma cláusula
LIMIT
. Se você estiver ordenando um número muito grande de valores, mas não precisar que todos eles sejam retornados, use uma cláusulaLIMIT
. Por exemplo, a consulta a seguir solicita um conjunto de resultados muito grande e gera um erroResources exceeded
. A consulta é classificada pela colunatitle
emmytable
. A colunatitle
contém milhões de valores.SELECT title FROM `my-project.mydataset.mytable` ORDER BY title;
Para remover o erro, use uma consulta como esta:
SELECT title FROM `my-project.mydataset.mytable` ORDER BY title DESC LIMIT 1000;
Use uma função de janela. Se você estiver ordenando um número muito grande de valores, use uma função de janela e limite os dados antes de chamar a função da janela. Por exemplo, a consulta a seguir lista os 10 usuários mais antigos do Stack Overflow e a classificação deles, com a conta mais antiga com a classificação mais baixa:
SELECT id, reputation, creation_date, DENSE_RANK() OVER (ORDER BY creation_date) AS user_rank FROM bigquery-public-data.stackoverflow.users ORDER BY user_rank ASC LIMIT 10;
Essa consulta leva aproximadamente 15 segundos para ser executada. Essa consulta usa
LIMIT
no final da consulta, mas não na função de janelaDENSE_RANK() OVER
. Por isso, a consulta exige que todos os dados sejam ordenados em um único nó de trabalho.Em vez disso, é preciso limitar o conjunto de dados antes de calcular a função de janela para melhorar o desempenho:
WITH users AS ( SELECT id, reputation, creation_date, FROM bigquery-public-data.stackoverflow.users ORDER BY creation_date ASC LIMIT 10) SELECT id, reputation, creation_date, DENSE_RANK() OVER (ORDER BY creation_date) AS user_rank FROM users ORDER BY user_rank;
A execução dessa consulta leva aproximadamente dois segundos e retorna os mesmos resultados da consulta anterior.
Uma ressalva é que a função
DENSE_RANK()
classifica os dados em anos. Portanto, para classificar dados que se estendem por vários anos, essas consultas não fornecem resultados idênticos.
Dividir consultas complexas em consultas menores.
Prática recomendada: aproveite os recursos de consulta de várias instruções e os procedimentos armazenados para executar os cálculos projetados como uma consulta complexa. como múltiplas consultas menores e mais simples.
Consultas complexas, funções REGEX
e subconsultas ou junções em camadas podem ser lentas
e consumir muitos recursos. Tentar encaixar todos os cálculos em uma grande
instrução SELECT
, por exemplo, para torná-la uma visualização, às vezes é um antipadrão
e pode resultar em uma consulta lenta e com uso intensivo de recursos. Em casos extremos, o plano de consulta interno fica tão complexo que o BigQuery não consegue executá-lo.
A divisão de uma consulta complexa permite materializar resultados intermediários em variáveis ou tabelas temporárias. Use esses resultados intermediários em outras partes da consulta. É cada vez mais útil quando esses resultados são necessários em mais de um local da consulta.
Muitas vezes, é possível expressar melhor a intenção real de partes da consulta, sendo as tabelas temporárias os pontos de materialização de dados.
Usar campos aninhados e repetidos
Para informações sobre como desnormalizar o armazenamento de dados usando campos aninhados e repetidos, consulte Usar campos aninhados e repetidos.
Usar tipos de dados INT64
em agrupamentos
Prática recomendada: use tipos de dados INT64
em agrupamentos em vez de tipos de
dados STRING
para reduzir os custos e melhorar o desempenho de comparação.
O BigQuery não indexa chaves primárias como bancos de dados tradicionais. Portanto,
quanto maior a coluna de junção, mais tempo a comparação leva. Sendo assim, usar
tipos de dados INT64
em junções é mais barato e mais eficiente do que usar tipos de dados
STRING
.
Reduzir resultados de consultas
É possível reduzir os resultados da consulta usando as opções descritas nas seções a seguir.
Materializar grandes conjuntos de resultados
Prática recomendada: pense cuidadosamente antes de materializar grandes conjuntos de resultados em uma tabela de destino. Gravar grandes conjuntos de resultados causa impactos de desempenho e custo.
O BigQuery limita os resultados em cache a aproximadamente 10 GB compactados. As consultas que retornam resultados maiores ultrapassam esse limite e
frequentemente resultam no seguinte erro: Response too large
.
Esse erro geralmente ocorre quando você seleciona um grande número de campos de uma tabela com uma quantidade considerável de dados. Problemas ao gravar resultados em cache também podem ocorrer em consultas em estilo ETL que normalizam dados sem redução ou agregação.
Você pode superar a limitação de tamanho dos resultados armazenados em cache usando as seguintes opções:
- usando filtros para limitar o conjunto de resultados;
- usando uma cláusula
LIMIT
para reduzir o conjunto de resultados, especialmente se estiver usando uma cláusulaORDER BY
; - gravando os dados de saída em uma tabela de destino.
Você pode navegar pelos resultados usando a API REST do BigQuery. Para mais informações, consulte Como fazer a paginação de dados da tabela.
Usar o BI Engine
Para acelerar ainda mais suas consultas SQL, armazenando em cache os dados que você usa com mais
frequência, adicione uma reserva do BI Engine
ao projeto em que as consultas estão sendo calculadas.
O BigQuery BI Engine usa um mecanismo de consulta vetorial para acelerar o desempenho da consulta SELECT
.
Evitar padrões anti-SQL
As práticas recomendadas a seguir apresentam uma orientação sobre como evitar os antipadrões de consulta que afetam o desempenho no BigQuery.
Evitar mesclagens automáticas
Prática recomendada: em vez de usar mesclagens automáticas, use uma
função (analítica) de janela ou o
operador PIVOT
.
Em geral, as mesclagens automáticas são usadas para calcular relacionamentos dependentes de linha. O resultado disso é uma possível quadruplicação do número de linhas de saída. Esse aumento nos dados de saída pode prejudicar o desempenho.
Evitar correlações
Prática recomendada: evite uniões que gerem mais saídas do que entradas. Quando um
CROSS JOIN
for necessário, agregue seus dados previamente.
As uniões cruzadas são consultas em que cada linha da primeira tabela é unida a cada linha na segunda tabela. Há chaves não únicas nos dois lados. O pior caso de saída é o número de linhas na tabela esquerda multiplicado pelo número de linhas na tabela direita. Em casos extremos, a consulta pode não terminar.
Se o job de consulta for concluído, a explicação do plano de consulta mostrará linhas de saída versus linhas de entrada. É possível confirmar um produto cartesiano (em inglês)
modificando a consulta de maneira a imprimir o número de linhas em cada lado da cláusula JOIN
, agrupado pela chave de união.
Para evitar problemas de desempenho resultantes de uniões que geram mais saídas do que entradas, siga estas recomendações:
- Use uma cláusula
GROUP BY
para agregar dados previamente. - Use uma função de janela. Em diversas situações, as funções de janela são mais eficientes do que usar uma correlação. Para mais informações, consulte Funções de janela.
Evitar instruções DML que atualizam ou inserem linhas individuais
Prática recomendada: evite instruções DML que atualizam ou inserem linhas únicas. Agrupe em lote suas atualizações e inserções.
O uso de instruções DML específicas de pontos é uma tentativa de tratar o BigQuery como um processamento de transações on-line (OLTP, na sigla em inglês). O BigQuery se concentra no processamento analítico on-line (OLAP, na sigla em inglês) usando varreduras de tabelas em vez de pesquisas de pontos. Se você precisa de comportamento semelhante ao do OLTP (atualizações ou inserções de linha única), considere um banco de dados projetado para comportar casos de uso de OLTP, como o Cloud SQL.
As instruções DML do BigQuery destinam-se a atualizações em massa. As declarações DML UPDATE
e DELETE
no BigQuery são orientadas para reescrever seus dados periodicamente, e não para mutações de uma única linha. A instrução DML INSERT
deve ser utilizada com moderação. As inserções consomem as mesmas
cotas de modificação
que os jobs de carregamento. Se seu caso de uso envolver inserções frequentes de uma única linha,
considere transmitir seus dados.
Se o agrupamento de suas declarações UPDATE
resultar em muitas tuplas em consultas muito longas, você pode abordar o limite de comprimento da consulta de 256 KB. Para contornar esse limite, considere se as atualizações podem ser tratadas com base em um critério lógico em vez de uma série de substituições de tuplas diretas.
Por exemplo, você pode carregar seu conjunto de registros de substituição em outra tabela e, em seguida, escrever a declaração DML para atualizar todos os valores na tabela original se as colunas não atualizadas corresponderem. Por exemplo, se os dados originais estivessem na tabela t
e as
atualizações estivessem na tabela u
, a consulta teria a aparência a seguir:
UPDATE dataset.t t SET my_column = u.my_column FROM dataset.u u WHERE t.my_key = u.my_key
Usar nomes de alias para colunas com nomes semelhantes
Prática recomendada: use aliases de coluna e tabela ao trabalhar com colunas de nomes semelhantes em consultas, incluindo subconsultas.
Os aliases ajudam a identificar quais colunas e tabelas são referenciadas, além da referência inicial da coluna. O uso de aliases pode ajudá-lo a entender e resolver problemas na consulta SQL, incluindo encontrar as colunas usadas em subconsultas.
Especificar restrições no esquema da tabela
Quando os dados da tabela contiverem restrições, especifique as restrições no esquema da tabela. O mecanismo de consulta pode otimizar planos de consulta usando restrições de tabela.
Especificar restrições de chave primária e chave externa
Especifique as restrições de chave no esquema da tabela quando os dados dela atenderem aos requisitos de integridade de dados das restrições de chave primária ou chave externa. O mecanismo de consulta pode usar as restrições de chave para otimizar os planos de consulta. Encontre informações detalhadas na postagem do blog Mesclar otimizações com chaves primárias e chaves externas do BigQuery.
O BigQuery não verifica automaticamente a integridade de dados. Por isso, garanta que eles atendam às restrições especificadas no esquema da tabela. Se você não mantiver a integridade de dados em tabelas com restrições especificadas, os resultados da consulta poderão ser imprecisos.
A seguir
- Saiba como otimizar os custos.
- Saiba como otimizar o armazenamento.
- Saiba como otimizar funções.