Menos é mais. - Ludwig Mies van der Rohe

Não há declaração mais verdadeira sobre os objetivos de um otimizador de consultas. Fazer menos: Menos memória, menos CPU, menos disco, menos E/S, menos instruções, menos partições, menos excesso. Menos tudo para o plano de consulta que ele cria. Essa é a luz que orienta o SQL e o Otimizadores de NoSQL.

No Couchbase 6.5, anunciamos o otimizador baseado em custo (CBO-preview) para N1QL no serviço de consulta. Aqui, tentei responder às perguntas dos usuários de NoSQL que não estão familiarizados com os benefícios do CBO.

  • Por que realmente precisamos de um CBO?
  • Quais são as implicações de desempenho sem um CBO?

O tópico é vasto. As respostas aqui são breves e não exaustivas. 

Em 2019, quando isso for importante - como chegar a tempo ao recital do seu filho ou a um jogo de futebol - você usaria um mapa de direção estático que não leva em conta o tráfego? O otimizador de rotas do Google Maps otimizará o tempo. Os otimizadores tentam criar um plano para executar a consulta com o mínimo de recursos: CPU, memória. Sabendo disso, por que você aceitaria uma regra estática (ou forma de consulta!) em sua carga de trabalho de banco de dados crítica para os negócios?

O otimizador de banco de dados toma decisões. Essas decisões têm implicações importantes no desempenho da consulta, na taxa de transferência do sistema e na sua capacidade de cumprir os SLAs. Os bancos de dados com um otimizador melhor facilitarão o desenvolvimento, o gerenciamento e o cumprimento dos SLAs.

O SQL é a linguagem de quarta geração mais bem-sucedida. Como linguagem, ela é extraordinariamente flexível, mesmo quando o esquema subjacente não o é. Você pode selecionar, unir, projetar qualquer relação (tabela ou relações intermediárias) sem planejar todas as combinações com antecedência. É possível selecionar, unir e projetar qualquer relação (tabela ou relações intermediárias) sem planejar todas as combinações com antecedência. Isso beneficia o desenvolvimento de aplicativos e a análise de dados. Isso O artigo explica como os principais bancos de dados NoSQL implementaram vários elementos do SQL. Portanto, até mesmo os bancos de dados NoSQL precisam se preocupar com a otimização. 

Com a extraordinária flexibilidade de uma linguagem de consulta, vem a extraordinária responsabilidade de otimizar e executar as consultas de forma eficiente. As implementações iniciais do SQL usavam otimizadores baseados em regras. Isso levou à complexidade das regras, às dicas do otimizador definidas pelo usuário e aos problemas de eficiência do plano de consulta para consultas complexas. O otimizador baseado em custos (CBO) mudou tudo, otimizando corretamente a consulta para uma variedade de dados, distorções de dados e cargas de trabalho. Não é exagero dizer que o RDBMS não teria sido tão bem-sucedido em lidar com casos de uso tão ricos a um custo tão baixo sem um CBO. O mesmo se aplica aos sistemas NoSQL com otimizadores. 

O otimizador de banco de dados toma decisões. Decisões ruins têm enormes implicações negativas no desempenho. Para cargas de trabalho do mundo real, as decisões baseadas em estatísticas são muito melhores do que as decisões baseadas em regras. As estatísticas mostram isso!

O otimizador, em termos gerais, faz o seguinte:

  1. REESCRITA: Reescreva a consulta em sua forma equivalente ideal para facilitar a otimização. Isso inclui a avaliação de filtros constantes, a conversão de uniões, o achatamento de subconsultas, o dobramento de subconsultas e muito mais. O tipo de reescrita depende dos recursos específicos e das nuances do otimizador nas fases subsequentes. 
  2. CAMINHO DE ACESSO: Selecionar entre os índices disponíveis ou a varredura completa (varredura de índice primário no caso do Couchbase) para cada espaço-chave (equivalente a tabelas). Aqui, selecionamos um ou mais índices para cada espaço de chave, decidimos os predicados (intervalos) para cada solicitação de varredura e decidimos se ela está cobrindo ou não.
  3. JOIN ORDER: O objetivo é limitar o tamanho do conjunto de resultados intermediários. Os JOINS são executados em dois espaços-chave (tabelas) de cada vez. Dependendo do tipo de junção, podemos alterar a ordem sem alterar o significado e o resultado da consulta. Por exemplo, ((t1 INNER JOIN t2) INNER JOIN t3) é o mesmo que ((t3 INNER JOIN t2) INNER JOIN t1). Aqui, selecionamos a sequência na qual as uniões são executadas. O N1QL Optimizer ainda não reordena as uniões.
  4. JOIN TYPE: Cada mecanismo de consulta é capaz de realizar determinados tipos de junções. O serviço de consulta e o serviço de análise do Couchbase suportam o loop aninhado (NLJ) e a junção de hash (HJ). Para o serviço de consulta, o loop aninhado é o padrão e, para o serviço de análise, a junção de hash é o padrão. Depois que o tipo de união é escolhido, outras decisões precisam ser tomadas sobre a ordem dentro da união. Para NLJ, precisamos decidir qual tabela é a tabela externa e qual é a tabela interna. Normalmente, queremos escolher a tabela (espaço-chave) com um conjunto de resultados menor para ser a tabela externa. Para HJ, precisamos decidir qual tabela é o lado de construção (tabela hash) e a outra se torna o lado de sondagem do plano. 
  5. Há considerações adicionais para otimizações (por exemplo, otimização da primeira linha quando a cláusula LIMIT é especificada).
  6. CRIAR ÁRVORE DE EXECUÇÃO: Por fim, crie a árvore de execução da consulta (plano) com os operadores e os valores dos parâmetros que representam as decisões nas fases anteriores. 

Exemplo: 

SELECIONAR id, endereço DE cliente ONDE código postal = 57020;

A mesma consulta pode operar em uma única linha, milhões de linhas ou bilhões de linhas. É possível que essa consulta seja tão simples quanto possível, mas a complexidade está escondida logo abaixo da superfície. O otimizador pode ter muitas opções para chegar aos dados.

  1. Uma varredura completa da tabela é sempre uma opção. Se a tabela de clientes tiver apenas algumas linhas que caibam em uma ou duas páginas do banco de dados, uma varredura completa da tabela pode ser o caminho mais eficiente para chegar aos dados.
  2. Imagine que você tenha um índice na tabela.
    1. CRIAR ÍNDICE i1 ON cliente(código postal)

Você poderia pensar que o caminho do índice, no qual você primeiro examina o índice para encontrar o rowid das linhas que correspondem ao predicado e, em seguida, obtém as linhas para projetar as colunas de adição (id, endereço), seria o melhor. Não tão rápido. E se a tabela tiver um milhão de linhas e TODAS elas tiverem exatamente o mesmo código postal - 57020? Nesse caso, o caminho de acesso ao índice é realmente mais caro do que uma varredura de tabela.

Agora, considere uma pequena modificação na consulta.

SELECIONAR id, endereço DE cliente ONDE código postal = 57020 e yob < 1980;

Considere que você tem os seguintes índices:

A escolha de um caminho de acesso válido para o otimizador será:

  • Cada índice de i1 a i8 é um caminho de acesso válido
  • Uma varredura de tabela é sempre uma opção.
  • vários índices combinados

De repente, não é fácil escolher o melhor índice para a consulta, mesmo para essa consulta simples. Portanto, um otimizador baseado em regras mantém um conjunto de regras e segue essas regras de forma consistente para chegar ao melhor plano. O conjunto de regras seguido por Otimizador baseado em regras N1QL estão bem documentados. 

Essas regras não foram definidas em pedra desde o primeiro dia. Você começa a preferir caminhos de índice, índices com a maioria das chaves etc. Mesmo assim, você terá conflitos. 

Exemplo:

Consulta: SELECIONAR id, endereço DE cliente ONDE código postal = 57020 e yob < 1980; 

Índices: 

CRIAR ÍNDICE i7 ON customer(postalcode, yob, id, endereço); CRIAR ÍNDICE i8 ON cliente(yob, Código postal, id, endereço);

Um otimizador baseado em regras não consegue descobrir qual desses índices é o mais eficiente. Tudo se resume à distorção dos dados: A seleção do índice em um banco de dados não será ideal em outro banco de dados.

Exemplo:

Consulta:

 

Na cláusula FROM fornecida, todas as ordens a seguir são válidas. Qual delas o otimizador deve escolher?

  1. ((order INNER JOIN customer) INNER JOIN demo)
  2. ((customer INNER JOIN order) INNER JOIN demo)
  3. ((order INNER JOIN demo) INNER JOIN customer)
  4. ((customer INNER JOIN demo) INNER JOIN order)
  5. ((demo INNER JOIN order) INNER JOIN customer)
  6. ((demo INNER JOIN customer) INNER JOIN order)

As opções aumentam e a seleção se torna mais complicada à medida que o número de keyspaces (ou tabelas) aumenta na cláusula FROM. Se a ordem estiver errada, os resultados intermediários poderão ser enormes, mas a maior parte deles será descartada posteriormente. Por exemplo, na consulta acima, a ordem de junção com o cliente primeiro criará um enorme conjunto de resultados intermediários porque estamos interessados apenas em clientes casados e com formação universitária. Uma ordem de junção ruim afeta negativamente a latência da sua consulta e a taxa de transferência do sistema

Exemplo:

Consulta:

 

Há duas decisões a serem tomadas aqui. Tipo de JOIN e ordem das tabelas. Sem conhecer as estatísticas de cada uma delas, é impossível tomar uma decisão inteligente. Portanto, os otimizadores baseados em regras simplesmente usarão um método como padrão e dependerão do usuário para alterar o padrão. Isso é ineficiente e inviável para consultas grandes. As implicações desse fato para o desempenho são enormes: de segundos a minutos ou de minutos a horas.

Novamente, as estimativas estatísticas vêm em socorro. Em aplicativos corporativos, são comuns consultas com muitos espaços-chave (tabelas) e predicados complexos.  

Conclusão

Para cargas de trabalho do mundo real, as decisões baseadas em estatísticas são muito melhores do que as decisões baseadas em regras. E ponto final. Esse é o motivo pelo qual N1QL implementou a CBO. Baixar Couchbase 6.5 agora e veja por si mesmo. 

E antes de optar por um banco de dados NoSQL, pergunte ao fornecedor:  Você tem um otimizador baseado em custos?

Referências

  1. A eficácia irracional do SQL em bancos de dados NoSQL: Um estudo comparativo. https://www.couchbase.com/blog/the-unreasonable-effectiveness-of-sql-in-nosql-databases/
  2. A eficácia irracional do SQL https://www.couchbase.com/blog/unreasonable-effectiveness-of-sql/ 
  3. Faça o download do Couchbase 6.5: https://couchbase.com/downloads?family=server&product=couchbase-server-developer
  4. Uma visão geral da otimização de consultas em sistemas relacionais. https://cs.stanford.edu/people/chrismre/cs345/rl/chaudhuri98.pdf

Autor

Postado por Keshav Murthy

Keshav Murthy é vice-presidente de P&D da Couchbase. Anteriormente, ele trabalhou na MapR, IBM, Informix e Sybase, com mais de 20 anos de experiência em design e desenvolvimento de bancos de dados. Ele liderou a equipe de P&D de SQL e NoSQL na IBM Informix. Recebeu dois prêmios President's Club na Couchbase e dois Outstanding Technical Achievement Awards na IBM. Keshav é bacharel em Ciência da Computação e Engenharia pela Universidade de Mysore, Índia, detém dez patentes nos EUA e tem três patentes pendentes nos EUA.

Deixar uma resposta