menu
languageEspañol
save_alt Descargar

Explain Visualizer

Visualiza los planes de ejecución de consultas como un diagrama interactivo y encuentra las partes lentas de una consulta.

por rathboma Plugin de la comunidad

Readme

Este texto lo escribió un tercero y se tradujo automáticamente. Lee el original en inglés

Un plugin de Beekeeper Studio que convierte los planes de ejecución de consultas en un diagrama interactivo, para que encuentres fácilmente la parte lenta de una consulta.

Recorrido: haz clic derecho en una consulta, abre su plan, ejecuta Explain Analyze y salta al paso lento

  • Diagrama del plan, coloreado según la parte del tiempo de ejecución (o del costo estimado) de cada paso. Los enlaces más anchos llevan más filas.
  • Vista de tabla al estilo de explain.depesz.com, ordenable por tiempo propio.
  • Detalles de cada paso: tiempo propio y total, filas reales vs. estimadas, loops, costos, condiciones, buffers, uso de memoria y disco, workers.
  • Insights que señalan problemas comunes: escaneos completos que descartan la mayoría de las filas, tablas escaneadas una vez por cada fila de un join, sorts y hashes que se desbordan a disco, subconsultas dependientes, índices temporales que SQLite construye en cada ejecución, grandes errores de estimación de filas y más.
  • Salida cruda del plan, lista para copiar o guardar.

Más en las capturas de pantalla de abajo.

Bases de datos compatibles

Base de datos Explain Explain Analyze Pegar un plan
PostgreSQL Sí Sí JSON y texto de psql
MySQL Sí 8.0.18+, sentencias de lectura Tree, JSON (ambas versiones) y la tabla clásica
MariaDB Sí 10.1+, sentencias de lectura JSON y la tabla clásica
SQLite, LibSQL Sí No: SQLite no tiene tiempos por paso El árbol del shell sqlite3, o las filas del resultado
SQL Server Planeado    

Probado con PostgreSQL 12, 17 y 18; MySQL 5.7, 8.0.17, 8.0, 8.4 y 9.7; MariaDB 10.4, 10.6 y 11.4; y SQLite 3.16, 3.27 y 3.45. El plugin está construido sobre un modelo de plan neutral respecto a la base de datos, así que cada base de datos solo necesita un adaptador (consulta Agregar una base de datos).

Cómo usarlo

Hay varias formas de entrar:

  • Editor de consultas: haz clic derecho en una consulta y elige Visualize Explain Plan. Se usa el texto seleccionado o la sentencia bajo el cursor.
  • Menú de nueva pestaña: abre Explain Visualizer, escribe una sentencia y elige Explain o Explain Analyze.
  • Cuadrícula de resultados: después de ejecutar EXPLAIN tú mismo, haz clic derecho en el plan y elige Visualize Explain Plan:
    • un plan en una celda (FORMAT JSON de PostgreSQL, tree o JSON de MySQL): haz clic derecho en la celda;
    • un plan repartido en filas (salida de texto de PostgreSQL): haz clic derecho en el encabezado de la columna QUERY PLAN;
    • una tabla (EXPLAIN clásico de MySQL, EXPLAIN QUERY PLAN de SQLite): selecciona todas sus filas y columnas y luego haz clic derecho en la selección. Para SQLite también funciona solo la columna de detalle, pero sin anidamiento.
  • Pegar plan: pega la salida de EXPLAIN, por ejemplo de psql, del shell de mysql o sqlite3, o de un colega.

El menú contextual del editor de consultas con Visualize Explain Plan

Explain y Explain Analyze

Explain le pide a la base de datos su plan sin ejecutar la sentencia. Siempre es seguro, así que se ejecuta automáticamente cuando la pestaña se abre desde el editor.

Explain Analyze ejecuta la sentencia y mide cada paso. Cómo se mantiene seguro depende de la base de datos (ver abajo). Si el texto del editor ya empieza con EXPLAIN ANALYZE (o su equivalente en la base de datos), el plugin lo usa; cuando la sentencia no se puede analizar, se hace solo un explain y el motivo se muestra debajo del editor.

PostgreSQL

El plugin envía EXPLAIN ANALYZE ...; ROLLBACK; en una sola solicitud, así que ambas sentencias comparten una transacción implícita y todo lo que la sentencia cambió se revierte. Eso incluye los SELECT simples, que pueden llamar funciones que escriben. Si la sentencia falla, la transacción se aborta sola y la conexión del pool queda limpia. Los efectos fuera de la transacción, como los valores de secuencias, no se deshacen.

Las sentencias que cambian datos (INSERT, UPDATE, DELETE, MERGE, CTEs que modifican datos…) solo se analizan después de una confirmación. El plugin lee la sentencia con un lexer que sigue las reglas propias de PostgreSQL para strings, comentarios y dollar quotes, y rechaza la entrada que contiene más de una sentencia.

En conexiones de solo lectura, Beekeeper Studio no permite el ROLLBACK, así que las sentencias que cambian datos solo se pueden explicar y Explain Analyze nunca se ejecuta automáticamente. Ahí no se detecta un SELECT que llama a una función que escribe, igual que al ejecutarlo en una pestaña de consulta.

El menú Options activa o desactiva BUFFERS, VERBOSE, SETTINGS y WAL. Las opciones que el servidor no soporta se deshabilitan. Las sentencias con parámetros $1 se explican con GENERIC_PLAN en PostgreSQL 16+.

MySQL y MariaDB

El plugin lee la versión del servidor y usa el formato de plan más reciente que tenga:

Servidor Explain Explain Analyze
MySQL 8.0.18+ EXPLAIN FORMAT=TREE EXPLAIN ANALYZE
MySQL 5.6 a 8.0.17 EXPLAIN FORMAT=JSON No disponible
MariaDB 10.1+ EXPLAIN FORMAT=JSON ANALYZE FORMAT=JSON
Servidores más antiguos La tabla clásica de EXPLAIN No disponible

Los UPDATE y DELETE de una sola tabla no tienen tree; el plugin recurre a FORMAT=JSON y, en MySQL 9, cuyo JSON tampoco puede mostrarlos, a la tabla clásica. TiDB se reconoce y se rechaza en lugar de leerse mal.

Explain Analyze solo ejecuta sentencias de lectura. A diferencia de PostgreSQL, no se puede confiar en que MySQL deshaga una solicitud fallida: enviada como START TRANSACTION; ...; ROLLBACK;, una sentencia que falle dejaría la transacción abierta en una conexión del pool que luego usan otras pestañas. Un SELECT que llama a una función almacenada que escribe hace commit de lo que hizo la función, igual que al ejecutarlo en una pestaña de consulta. En MariaDB, el modo de solo lectura de Beekeeper Studio rechaza la sentencia ANALYZE, así que Explain Analyze no está disponible en conexiones de solo lectura.

La sentencia se lee con las reglas de MySQL: comentarios # y -- que terminan solo en un salto de línea, escapes con backslash, strings "..." y comentarios /*! ... */, cuyo contenido MySQL ejecuta. NO_BACKSLASH_ESCAPES y ANSI_QUOTES cambian cómo terminan los strings, y una conexión del pool puede tener cualquiera de los dos, así que una sentencia tiene que leerse como una sola sentencia, y como lectura, bajo cada combinación de ellos. El texto que un servidor podría leer de más de una forma se rechaza en lugar de adivinarse: un comentario condicionado por versión /*!50700 ... */ o exclusivo de MariaDB /*M! ... */, un retorno de carro dentro de un comentario --, o un carácter NUL.

MariaDB mide el tiempo de cada tabla en vez de cada paso, así que el tiempo de un join o de una tabla temporal es lo que la consulta tardó además de las tablas que leyó.

SQLite

EXPLAIN QUERY PLAN muestra qué tablas se leen, en qué orden y con qué índice. SQLite no reporta costos, conteos de filas ni tiempos, así que el diagrama muestra los pasos y la vista de tabla sus detalles, y no hay Explain Analyze. Los insights señalan índices temporales que SQLite construye en cada ejecución, tablas escaneadas una vez por fila de un join o de una subconsulta correlacionada, y sorts en un B-tree temporal. Las conexiones LibSQL usan el mismo motor.

Beekeeper Studio divide el texto de SQLite en sentencias con su propio parser antes de ejecutarlas, así que una sentencia con un punto y coma dentro (en un string, nombre o comentario) se rechaza: ningún divisor puede entonces encontrar una segunda sentencia en ella.

Capturas de pantalla

Explain Analyze, con el paso lento seleccionado y un insight sobre él:

Plan analizado con un sequential scan seleccionado y su insight

El mismo plan como tabla, ordenado por tiempo propio:

Vista de tabla ordenada por tiempo propio

Una consulta con un CTE, un InitPlan y un SubPlan, cada uno como su propia rama:

Plan con ramas de CTE, InitPlan y SubPlan

El plugin sigue el tema de Beekeeper Studio:

Un plan con el tema claro

Desarrollo

Requiere Node.js 22+ y Yarn 1.

yarn install
yarn dev        # builds with hot reload and links the plugin into Beekeeper Studio
yarn test       # unit tests
yarn build      # type-check and build into dist/

yarn dev usa @beekeeperstudio/vite-plugin, que crea un symlink de esta carpeta en el directorio de plugins de Beekeeper Studio.

Por defecto, Beekeeper Studio solo carga sus propios plugins permitidos. Para cargar este, agrega lo siguiente a tu archivo de configuración de usuario y reinicia la app:

[pluginSystem]
disabled = false

Luego conéctate a una base de datos: Visualize Explain Plan aparece en el menú contextual del editor de consultas y Explain Visualizer en el menú de nueva pestaña.

Harness

dev/harness.html hace las veces de Beekeeper Studio: carga el plugin en un iframe y responde a sus solicitudes con los mismos mensajes que usa la app, ejecutando consultas en bases de datos reales. Es la forma más rápida de trabajar en la UI, probar cada forma de abrir la pestaña, cambiar de base de datos y cambiar de tema.

docker run -d --name pg -e POSTGRES_PASSWORD=pw -p 5432:5432 postgres:17
psql postgres://postgres:pw@localhost:5432/postgres -f scripts/fixtures/seed.sql

docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=pw -e MYSQL_DATABASE=shop -p 3306:3306 mysql:8.4
mysql -h127.0.0.1 -uroot -ppw shop < scripts/fixtures/seed-mysql.sql

PG_URL=postgres://postgres:pw@localhost:5432/postgres \
MYSQL_URL=mysql://root:pw@127.0.0.1:3306/shop \
yarn harness

MARIADB_URL apunta el MariaDB del harness a otro servidor (si no, usa MYSQL_URL). SQLite no necesita nada: se llena una base de datos temporal desde scripts/fixtures/seed-sqlite.sql la primera vez, o SQLITE_PATH indica una.

Tests

  • tests/postgres parsea planes reales capturados de PostgreSQL 12, 17 y 18 (tests/fixtures/postgres). Cada fixture tiene el mismo plan en JSON y en texto, y los tests verifican que ambos produzcan el mismo árbol.
  • tests/mysql parsea planes capturados de MySQL 5.7, 8.0.17, 8.0, 8.4 y 9.7 y MariaDB 10.4, 10.6 y 11.4 (tests/fixtures/mysql). Cada fixture tiene todos los formatos que su servidor ofrece para una sentencia: tree, EXPLAIN ANALYZE, ambas versiones de JSON, el ANALYZE de MariaDB y la tabla clásica. Los tests verifican que los formatos coincidan.
  • tests/sqlite parsea filas de EXPLAIN QUERY PLAN de SQLite 3.16 (el formato anterior a 3.24), 3.27 y 3.45 (tests/fixtures/sqlite).
  • tests/integration ejecuta EXPLAIN en servidores reales: PostgreSQL cuando PG_URL está definido, MySQL o MariaDB cuando MYSQL_URL está definido, y SQLite siempre, mediante el node:sqlite integrado de Node.
  • tests/state cubre el comportamiento de la pestaña con un host falso.

Para capturar fixtures de otra versión de servidor, llénalo con datos y ejecuta su script de captura:

# PostgreSQL: seed with scripts/fixtures/seed.sql
PG_URL=postgres://... FIXTURE_PREFIX=pg16- node scripts/fixtures/capture-postgres.mjs
# MySQL or MariaDB: seed with scripts/fixtures/seed-mysql.sql; named after the server
MYSQL_URL=mysql://root:pw@127.0.0.1:3306/shop node scripts/fixtures/capture-mysql.mjs
# SQLite: uses Python's bundled SQLite, so older Python images give older SQLite
python3 scripts/fixtures/capture-sqlite.py
docker run --rm -v "$PWD:/w" -w /w python:3.6-stretch python3 scripts/fixtures/capture-sqlite.py

Cómo funciona

src/
  core/        database-neutral plan model, analysis, metrics, layout, formatting
  engines/     one adapter per database family, plus the registry
    postgres/  EXPLAIN builder, JSON and text parsers, timing rules, insights
    mysql/     version detection; tree, JSON (both versions) and table readers; insights
    sqlite/    EXPLAIN QUERY PLAN rows and shell text; insights
    shared/    helpers adapters build on: a configurable SQL lexer, statement
               checks, Explain Analyze policies, result readers, a JSON parser
               that keeps repeated keys
  host/        everything that talks to Beekeeper Studio
  state/       the tab's state and actions
  components/  Vue components: diagram, table, details, raw view

Cada adaptador convierte la salida de EXPLAIN de su base de datos al modelo Plan en src/core/plan.ts. Un PlanNode tiene un nombre de operación, un kind (scan, index, join, filter, sort…), estimaciones opcionales (totalCost, estimatedRows), mediciones opcionales (actualRows, loops, tiempos), los derivados inclusiveTime, exclusiveTime y exclusiveCost, además de condiciones y propiedades agrupadas para el panel de detalles. Cada métrica es opcional y la UI solo muestra lo que ofrece cada motor: un plan de SQLite no tiene números, así que la tabla muestra los detalles de cada paso en lugar de los costos.

El tiempo propio depende del motor, así que lo calcula el adaptador. Las reglas de PostgreSQL están en src/engines/postgres/timing.ts: los tiempos por loop se multiplican por los loops, los workers paralelos se cuentan una vez, el tiempo de un CTE se carga a los CTE scans que lo leen y el tiempo de un InitPlan se carga al nodo que lo evalúa. MySQL también reporta tiempos por loop y usa el computeLoopTimings compartido (src/core/metrics.ts), con las subconsultas de la lista del select sumadas encima de la consulta, que no las incluye. MariaDB mide tablas y consultas completas, no pasos; computeMixedTimings completa el resto.

Los tres formatos de MySQL se leen con los mismos nombres de operación: el texto tree y la versión 2 de JSON listan los mismos iteradores, y el JSON por bloques de consulta (y la tabla clásica, reconstruida en él) se convierte en una cadena de joins en el orden de las tablas. Las reglas de insights trabajan sobre esos nombres, no sobre la salida nativa.

src/core/analysis.ts agrega lo que comparten todos los motores: porcentajes de tiempo y costo, los pasos más lentos e insights de errores de estimación de filas.

Agregar una base de datos

No hace falta cambiar nada fuera de la carpeta del motor y del registro.

  1. Crea src/engines/<name>/ con un objeto que implemente ExplainEngine (src/engines/types.ts):
    • connectionTypes: los tipos de conexión de Beekeeper Studio que maneja, y products cuando cubre varios (MySQL y MariaDB).
    • inspect(sql, server): divide y clasifica la sentencia (¿cambia datos?). src/engines/shared/lexer.ts recibe un dialecto: estilos de comentario, comillas, placeholders.
    • analyzePolicy(statement, context): si Explain Analyze puede ejecutarla, qué preguntar antes y qué mostrar debajo del editor. src/engines/shared/analyzePolicy.ts tiene las políticas de rollback y de solo lectura.
    • explain(request, runQuery): envía el SQL que necesite la base de datos y parsea el resultado. Puede llamar a runQuery varias veces, por ejemplo para recurrir a un formato más antiguo.
    • loadServerInfo(runQuery): la versión y cualquier otra cosa que el motor necesite saber (el SQL mode de MySQL). Extiende ServerInfo con tus propios campos.
    • canParse / parse: lee la salida pegada y los valores de la cuadrícula de resultados. El texto crudo que devuelve un parse debe volver a parsearse en el mismo plan: las pestañas restauradas se reconstruyen a partir de él.
    • options: opciones que se muestran en el menú Options, cada una con unavailable(server) cuando necesita un servidor más reciente.
  2. Mapea los operadores nativos a NodeKinds y completa las métricas que existan. Usa los helpers de src/core/metrics.ts para costos y tiempos donde apliquen, y llama a finishPlan() de src/core/analysis.ts al final.
  3. Regístralo en src/engines/registry.ts, agrega fixtures capturados de servidores reales (un script de captura en scripts/fixtures/) y tests como los demás.

Notas para SQL Server, que está planeado:

  • El cliente de SQL Server de Beekeeper Studio divide una solicitud en ; y puede ejecutar cada sentencia en una conexión distinta del pool. SET SHOWPLAN_XML ON debe ir solo en su batch y se aplica al siguiente batch en la misma conexión, así que un plan estimado necesita una nueva API del host que ejecute batches en una sola conexión.
  • SET STATISTICS XML ON con la sentencia en un solo batch (sin ;) da el plan real, pero ejecuta la sentencia y devuelve sus filas al plugin.
  • El XML de plan pegado desde SSMS (.sqlplan) no necesita soporte del host.

Publicación

  1. Ejecuta bin/make-release.sh. Sugiere la siguiente versión a partir de los tags existentes, define version en manifest.json y package.json, hace commit y sube un tag correspondiente como v0.1.0.
  2. El workflow de release compila, ejecuta los tests y crea un borrador de release en GitHub con manifest.json y explain-visualizer-<version>.zip. Publica el borrador.

yarn build && yarn package crea el mismo archivo localmente.

Licencia

GPL-3.0. Consulta LICENSE.