Vida útil de uma consulta do Spanner

Cliente

O Spanner oferece suporte a consultas SQL. Veja a seguir uma consulta de amostra:

SELECT s.SingerId, s.FirstName, s.LastName, s.SingerInfo
FROM Singers AS s
WHERE s.FirstName = @firstName;

A construção @firstName é uma referência a um parâmetro de consulta. Você pode usar um parâmetro de consulta em qualquer lugar em que um valor literal possa ser usado. O uso de parâmetros em APIs programáticas é recomendado. O uso de parâmetros de consulta ajuda a evitar ataques de injeção de SQL, e as consultas resultantes são mais propensas a se beneficiar de vários caches do lado do servidor. Consulte Como armazenar em cache, abaixo.

Os parâmetros de consulta precisam estar limitados a um valor quando a consulta é executada. Exemplo:

Statement statement =
    Statement.newBuilder("SELECT s.SingerId...").bind("firstName").to("Jimi").build();
try (ResultSet resultSet = dbClient.singleUse().executeQuery(statement)) {
 while (resultSet.next()) {
 ...
 }
}

Depois que o Spanner recebe uma chamada de API, ele analisa a consulta e os parâmetros vinculados para determinar qual nó do servidor do Spanner processará a consulta. O servidor envia um stream das linhas de resultados que são consumidas pelas chamadas para ResultSet.next().

Execução da consulta

A execução da consulta começa com a chegada de uma solicitação "executar consulta" em algum servidor Spanner. O servidor executa as seguintes etapas:

  • Validação do pedido
  • Análise do texto da consulta
  • Geração de uma álgebra de consulta inicial
  • Geração de uma álgebra de consulta otimizada
  • Geração de um plano de consulta executável
  • Execução do plano (verificação das permissões, leitura dos dados, codificação dos resultados etc.)

Diagrama de fluxo de execução de consulta que mostra o cliente, o servidor raiz e os servidores folha

Análise

O analisador SQL analisa o texto da consulta e o converte em uma árvore de sintaxe abstrata. Ele extrai a estrutura de consulta básica (SELECT … FROM … WHERE …) e faz verificações sintáticas.

Álgebra

O sistema de tipos do Spanner pode representar escalares, matrizes, estruturas etc. A álgebra de consulta define operadores para verificações de tabelas, filtragem, classificação/agrupamento, todos os tipos de mesclagens, agregação e muito mais. A álgebra de consulta inicial é criada da saída do analisador. As referências de nomes de campo na árvore de análise são resolvidas com o esquema do banco de dados. Esse código também verifica erros de semântica, como número incorreto de parâmetros, incompatibilidades na digitação etc.

A próxima etapa ("otimização da consulta") usa a álgebra inicial e gera uma álgebra otimizada. Isso pode ser mais simples, mais eficiente ou mais adequado às capacidades do mecanismo de execução. Por exemplo, a álgebra inicial pode especificar apenas uma "junção", enquanto a álgebra otimizada especifica uma "junção de hash".

Execução

O plano de consulta executável final é construído da álgebra reescrita. Basicamente, o plano executável é um gráfico acíclico direcionado de "iteradores". Cada iterador expõe uma sequência de valores. Os iteradores podem utilizar entradas para produzir saídas (por exemplo, iterador de classificação). As consultas que envolvem uma única divisão podem ser executadas por um único servidor (o que mantém os dados). O servidor verificará os intervalos de várias tabelas, executará junções, realizará a agregação e fará todas as outras operações definidas pela álgebra de consulta.

As consultas que envolvem várias divisões serão fatoradas em vários pedaços. Uma parte da consulta continuará sendo executada no servidor principal (raiz). Outras subconsultas parciais são transferidas para nodes de folha (aqueles que contêm as divisões sendo lidas). Essa entrega pode ser recursivamente aplicada a consultas complexas, resultando em uma árvore de execuções de servidor. Em todos os servidores, há um consentimento em relação a um carimbo de data/hora para que os resultados da consulta sejam um instantâneo consistente dos dados. Cada servidor de folha envia um stream dos resultados parciais. Para consultas envolvendo agregação, podem ser resultados parcialmente agregados. O servidor raiz da consulta processa os resultados dos servidores de folha e executa o restante do plano de consulta. Para mais informações, consulte Planos de execução de consulta.

Quando uma consulta envolve várias divisões, o Spanner pode executá-la em paralelo entre as divisões. O grau de paralelismo depende do intervalo de dados que a consulta verifica, do plano de execução da consulta e da distribuição de dados nas divisões. O Spanner define automaticamente o grau máximo de paralelismo de uma consulta com base no tamanho da instância e na configuração da instância (regional ou multirregional) para alcançar o desempenho ideal de consulta e evitar a sobrecarga da CPU.

Armazenamento em cache

Muitos dos artefatos do processamento de consulta são automaticamente armazenados em cache e reutilizados para consultas subsequentes. Isso inclui álgebras de consulta, planos de consulta executáveis etc. O armazenamento em cache é baseado no texto da consulta, nomes e tipos de parâmetros vinculados, e assim por diante. É por isso é melhor usar parâmetros associados (como @firstName, no exemplo acima) em vez de valores literais no texto da consulta. O primeiro pode ser armazenado em cache uma vez e reutilizado independentemente do valor vinculado real. Consulte Como otimizar o desempenho da consulta do Spanner para mais detalhes.

Tratamento de erros

O fluxo de linhas de resultados do método executeQuery pode ser interrompido por vários motivos: erros de rede temporários, transferência de uma divisão de um servidor para outro (por exemplo, balanceamento de carga), reinicializações do servidor (por exemplo, upgrade para uma nova versão) etc. Para ajudar na recuperação desses erros, o Spanner envia "tokens de retomada" opacos com lotes de dados de resultado parcial. Esses tokens de retomada podem ser usados ao repetir a consulta para continuar de onde a consulta interrompida parou. Se você estiver usando as bibliotecas de cliente do Spanner, isso será feito automaticamente. Assim, os usuários da biblioteca de cliente não precisam se preocupar com esse tipo de falha temporária.