Ler os planos EXPLAIN do Aurora DSQL - Amazon Aurora DSQL

Ler os planos EXPLAIN do Aurora DSQL

Entender como ler os planos EXPLAIN é fundamental para otimizar a performance das consultas. Nesta seção, vamos examinar exemplos reais de planos de consulta do Aurora DSQL, mostrar como os diferentes tipos de verificação se comportam, explicar onde os filtros são aplicados e destacar as oportunidades de otimização.

Exemplo de verificação completa

O Aurora DSQL tem tanto verificações sequenciais, que são funcionalmente idênticas ao PostgreSQL, quanto verificações completas. A única diferença entre elas é que as verificações completas podem utilizar filtragem extra no armazenamento. Por isso, quase sempre ela é selecionada em detrimento de verificações sequenciais. Devido à semelhança, abordaremos apenas exemplos das verificações completas mais interessantes.

As verificações completas serão usadas principalmente em tabelas sem chave primária. Como as chaves primárias do Aurora DSQL são, por padrão, índices de cobertura completa, o Aurora DSQL provavelmente usará verificações somente de índice na chave primária em muitas situações em que o PostgreSQL usaria uma verificação sequencial. Como acontece com a maioria dos outros bancos de dados, uma tabela sem índices será mal escalada.

EXPLAIN SELECT account_id FROM transaction WHERE transaction_date > '2025-01-01' AND description LIKE '%external%';
                                                   QUERY PLAN
----------------------------------------------------------------------------------------------------------------
 Full Scan (btree-table) on transaction  (cost=125100.05..177933.38 rows=33333 width=16)
   Filter: (description ~~ '%external%'::text)
   -> Storage Scan on transaction (cost=12510.05..17793.38 rows=66666 width=16)
        Projections: account_id, description
        Filters: (transaction_date > '2025-01-01 00:00:00'::timestamp without time zone)
        -> B-Tree Scan on transaction (cost=12510.05..17793.38 rows=100000 width=30)

Esse plano mostra dois filtros aplicados em diferentes estágios. A condição transaction_date > '2025-01-01' é aplicada na camada de armazenamento, reduzindo o volume de dados exibidos. A condição description LIKE '%external%' é aplicada posteriormente no processador de consultas, após a transferência dos dados, tornando-a menos eficiente. Colocar filtros mais seletivos nas camadas de armazenamento ou índice geralmente melhora a performance.

Exemplo de verificação somente de índice

As verificações somente de índice são os melhores tipos de verificação no Aurora DSQL, pois geram o menor número de idas e vindas até a camada de armazenamento e podem realizar a maior parte da filtragem. Mas só porque você vê “verificação somente de índice” não significa que tenha o melhor plano. Por conta de todos os diferentes níveis de filtragem que podem ocorrer, é essencial ainda prestar atenção aos diferentes locais em que a filtragem pode acontecer.

EXPLAIN SELECT balance FROM account WHERE customer_id = '4b18a761-5870-4d7c-95ce-0a48eca3fceb' AND balance > 100 AND status = 'pending';
                                  QUERY PLAN
-------------------------------------------------------------------------------
 Index Only Scan using idx1 on account  (cost=725.05..1025.08 rows=8 width=18)
   Index Cond: (customer_id = '4b18a761-5870-4d7c-95ce-0a48eca3fceb'::uuid)
   Filter: (balance > '100'::numeric)
   -> Storage Scan on idx1 (cost=12510.05..17793.38 rows=9 width=16)
        Projections: balance
        Filters: ((status)::text = 'pending'::text)
        -> B-Tree Scan on idx1 (cost=12510.05..17793.38 rows=10 width=30)
            Index Cond: (customer_id = '4b18a761-5870-4d7c-95ce-0a48eca3fceb'::uuid)

Nesse plano, a condição de índice, customer_id = '4b18a761-5870-4d7c-95ce-0a48eca3fceb', é avaliada primeiro durante a verificação do índice, que é o estágio mais eficiente porque limita o volume de dados lidos do armazenamento. O filtro de armazenamento, status = 'pending', é aplicado após a leitura dos dados, mas antes de serem enviados para a camada computacional, reduzindo o volume de dados transferidos. Por fim, o filtro do processador de consultas, balance > 100, é executado por último, após a movimentação dos dados, tornando-o o menos eficiente. Dessas, a condição de índice oferece a melhor performance porque controla diretamente o volume de dados verificados.

Exemplo de verificação de índice

As verificações de índice são semelhantes às verificações somente de índice, exceto que elas têm a etapa extra de exigir o acesso à tabela base. Como o Aurora DSQL pode especificar filtros de armazenamento, ele pode fazer isso tanto na chamada de índice quanto na chamada de pesquisa.

Para deixar isso claro, o Aurora DSQL apresenta o plano como dois nós. Dessa forma, é possível ver claramente o quanto a adição de uma coluna de inclusão ajudará em termos de linhas exibidas do armazenamento.

EXPLAIN SELECT balance FROM account WHERE customer_id = '4b18a761-5870-4d7c-95ce-0a48eca3fceb' AND balance > 100 AND status = 'pending' AND created_at > '2025-01-01';
                                                QUERY PLAN
----------------------------------------------------------------------------------------------------------
 Index Scan using idx1 on account  (cost=728.18..1132.20 rows=3 width=18)
   Filter: (balance > '100'::numeric)
   Index Cond: (customer_id = '4b18a761-5870-4d7c-95ce-0a48eca3fceb'::uuid)
   -> Storage Scan on idx1 (cost=12510.05..17793.38 rows=8 width=16)
        Projections: balance
        Filters: ((status)::text = 'pending'::text)
        -> B-Tree Scan on account (cost=12510.05..17793.38 rows=10 width=30)
            Index Cond: (customer_id = '4b18a761-5870-4d7c-95ce-0a48eca3fceb'::uuid)
   -> Storage Lookup on account (cost=12510.05..17793.38 rows=4 width=16)
        Filters: (created_at > '2025-01-01 00:00:00'::timestamp without time zone)
        -> B-Tree Lookup on transaction (cost=12510.05..17793.38 rows=8 width=30)

Esse plano mostra como a filtragem ocorre em vários estágios:

  • A condição de índice em customer_id filtra os dados com antecedência.

  • O filtro de armazenamento em status restringe ainda mais os resultados antes que eles sejam enviados para computação.

  • O filtro do processador de consultas em balance é aplicado posteriormente, após a transferência.

  • O filtro de pesquisa em created_at é avaliado ao buscar colunas adicionais da tabela base.

Adicionar colunas usadas com frequência como campos INCLUDE geralmente pode eliminar essa pesquisa e melhorar a performance.

Práticas recomendadas

  • Alinhe os filtros com as colunas indexadas para enviar a filtragem mais cedo.

  • Use as colunas INCLUDE para permitir verificações somente de índice e evitar pesquisas.

  • Mantenha as estatísticas atualizadas para garantir que as estimativas de custo e linha sejam precisas.

  • Evite consultas não indexadas em tabelas grandes para evitar verificações completas dispendiosas.