Lectura de los planes EXPLAIN de Aurora DSQL - Amazon Aurora DSQL

Lectura de los planes EXPLAIN de Aurora DSQL

Comprender cómo leer los planes EXPLAIN es clave para optimizar el rendimiento de las consultas. En esta sección, analizaremos ejemplos reales de planes de consultas de Aurora DSQL, mostraremos cómo se comportan los diferentes tipos de escaneo, explicaremos dónde se aplican los filtros y destacaremos las oportunidades de optimización.

Ejemplo de escaneo completo

Aurora DSQL tiene escaneos secuenciales, que funcionan de manera idéntica a PostgreSQL, así como escaneos completos. La única diferencia entre estos dos es que los escaneos completos pueden utilizar filtros adicionales en el almacenamiento. Debido a esto, casi siempre se selecciona por encima de los escaneos secuenciales. Debido a la similitud, solo trataremos ejemplos de los escaneos completos más interesantes.

Los escaneos completos se utilizarán principalmente en tablas sin clave principal. Como las claves principales de Aurora DSQL son índices de cobertura completa de forma predeterminada, lo más probable es que Aurora DSQL utilice escaneos solo indexados en la clave principal en muchas situaciones en las que PostgreSQL utilizaría un escaneo secuencial. Como ocurre con la mayoría de las demás bases de datos, una tabla sin índices se escalará mal.

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)

Este plan muestra dos filtros aplicados en diferentes etapas. La condición transaction_date > '2025-01-01' se aplica en la capa de almacenamiento, lo que reduce la cantidad de datos que se devuelven. La condición description LIKE '%external%' se aplica más adelante en el procesador de consultas, una vez transferidos los datos, lo que hace que sea menos eficiente. Insertar filtros más selectivos en las capas de almacenamiento o índice generalmente mejora el rendimiento.

Ejemplo de escaneo solo de índices

Los escaneos solo de índices son los tipos de escaneo más óptimos en Aurora DSQL, ya que producen el menor número de viajes de ida y vuelta a la capa de almacenamiento y son los que más filtran. Sin embargo, el hecho de que vea escaneo solo de índice no significa que tenga el mejor plan. Debido a los diferentes niveles de filtrado que se pueden producir, es esencial seguir prestando atención a los diferentes lugares en los que se puede producir el filtrado.

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)

En este plan, la condición de indexación (customer_id = '4b18a761-5870-4d7c-95ce-0a48eca3fceb'), se evalúa primero durante el escaneo del índice, que es la etapa más eficiente porque limita la cantidad de datos que se leen del almacenamiento. El filtro de almacenamiento, status = 'pending', se aplica después de leer los datos, pero antes de enviarlos a la capa de procesamiento, lo que reduce la cantidad de datos transferidos. Por último, el filtro del procesador de consultas, balance > 100, se ejecuta en último lugar, después de mover los datos, por lo que es el menos eficiente. De estas, la condición de índice es la que ofrece el mejor rendimiento, ya que controla directamente la cantidad de datos que se escanean.

Ejemplo de escaneo de índices

Los escaneos de índices son similares a los escaneos solo de índices, excepto que tienen el paso adicional de tener que llamar a la tabla base. Como Aurora DSQL puede especificar filtros de almacenamiento, puede hacerlo tanto en la llamada de índice como en la llamada de búsqueda.

Para que quede claro, Aurora DSQL presenta el plan como dos nodos. De esta forma, puede ver claramente en qué medida puede ayudar agregar una columna de inclusión en términos de filas devueltas desde el almacenamiento.

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)

Este plan muestra cómo se filtra en varias etapas:

  • La condición de indexación en customer_id filtra los datos de forma temprana.

  • El filtro de almacenamiento en status reduce aún más los resultados antes de que se envíen a informática.

  • El filtro del procesador de consultas en balance se aplica más adelante, después de la transferencia.

  • El filtro de búsqueda en created_at se evalúa al buscar columnas adicionales de la tabla base.

Agregar columnas de uso frecuente como campos de INCLUDE suele eliminar esta búsqueda y mejorar el rendimiento.

Prácticas recomendadas

  • Alinee los filtros con las columnas indexadas para acelerar el filtrado.

  • Utilice las columnas INCLUDE para permitir escaneos solo de índices y evitar búsquedas.

  • Mantenga las estadísticas actualizadas para garantizar que las estimaciones de costos y filas sean precisas.

  • Evite las consultas no indexadas en tablas grandes para evitar costosos escaneos completos.