menu
languageItaliano
save_alt Download

Explain Visualizer

Visualizza i piani di esecuzione delle query come un diagramma interattivo e trova le parti lente di una query.

di rathboma Plugin della community

Readme

Questo testo è stato scritto da terzi e tradotto automaticamente. Leggi l'originale in inglese

Un plugin per Beekeeper Studio che trasforma i piani di esecuzione delle query in un diagramma interattivo, così trovare la parte lenta di una query è facile.

Demo: clic destro su una query, apri il suo piano, esegui Explain Analyze e vai al passaggio lento

  • Diagramma del piano, colorato in base alla quota del tempo di esecuzione (o del costo stimato) di ogni passaggio. I collegamenti più larghi trasportano più righe.
  • Vista a tabella nello stile di explain.depesz.com, ordinabile per tempo proprio.
  • Dettagli per ogni passaggio: tempo proprio e totale, righe effettive vs. stimate, loop, costi, condizioni, buffer, uso di memoria e disco, worker.
  • Suggerimenti che segnalano i problemi più comuni: scansioni complete che scartano la maggior parte delle righe, tabelle scansionate una volta per ogni riga di una join, ordinamenti e hash che finiscono su disco, subquery dipendenti, indici temporanei che SQLite crea a ogni esecuzione, grandi errori di stima delle righe e altro.
  • Output grezzo del piano, pronto da copiare o salvare.

Altro negli screenshot qui sotto.

Database supportati

Database Explain Explain Analyze Incolla un piano
PostgreSQL Sì Sì JSON e testo di psql
MySQL Sì 8.0.18+, istruzioni di lettura Tree, JSON (entrambe le versioni) e la tabella classica
MariaDB Sì 10.1+, istruzioni di lettura JSON e la tabella classica
SQLite, LibSQL Sì No: SQLite non ha tempi per passaggio L'albero della shell sqlite3, o le righe del risultato
SQL Server Pianificato    

Testato con PostgreSQL 12, 17 e 18; MySQL 5.7, 8.0.17, 8.0, 8.4 e 9.7; MariaDB 10.4, 10.6 e 11.4; e SQLite 3.16, 3.27 e 3.45. Il plugin è costruito attorno a un modello di piano indipendente dal database, quindi ogni database ha bisogno solo di un adapter (vedi Aggiungere un database).

Come usarlo

Ci sono diversi modi per iniziare:

  • Editor di query: fai clic destro su una query e scegli Visualize Explain Plan. Viene usato il testo selezionato, o l'istruzione sotto il cursore.
  • Menu nuova scheda: apri Explain Visualizer, scrivi un'istruzione e scegli Explain o Explain Analyze.
  • Griglia dei risultati: dopo aver eseguito EXPLAIN da te, fai clic destro sul piano e scegli Visualize Explain Plan:
    • un piano in una cella (FORMAT JSON di PostgreSQL, tree o JSON di MySQL): fai clic destro sulla cella;
    • un piano distribuito su più righe (l'output testuale di PostgreSQL): fai clic destro sull'intestazione della colonna QUERY PLAN;
    • una tabella (l'EXPLAIN classico di MySQL, l'EXPLAIN QUERY PLAN di SQLite): seleziona tutte le righe e colonne, poi fai clic destro sulla selezione. Per SQLite funziona anche la sola colonna detail, senza l'annidamento.
  • Paste Plan: incolla l'output di EXPLAIN, ad esempio da psql, dalla shell mysql o sqlite3, o da un collega.

Il menu contestuale dell'editor di query con Visualize Explain Plan

Explain ed Explain Analyze

Explain chiede al database il suo piano senza eseguire l'istruzione. È sempre sicuro, quindi viene eseguito automaticamente quando la scheda si apre dall'editor.

Explain Analyze esegue l'istruzione e misura ogni passaggio. Come resta sicuro dipende dal database, vedi sotto. Se il testo nell'editor inizia già con EXPLAIN ANALYZE (o l'equivalente del database), il plugin lo usa; quando l'istruzione non può essere analizzata viene invece eseguito l'explain, e il motivo è mostrato sotto l'editor.

PostgreSQL

Il plugin invia EXPLAIN ANALYZE ...; ROLLBACK; come singola richiesta, così entrambe le istruzioni condividono una transazione implicita e tutto ciò che l'istruzione ha modificato viene annullato. Questo vale anche per le semplici SELECT, che possono chiamare funzioni che scrivono. Se l'istruzione fallisce, la transazione viene interrotta da sola e la connessione del pool resta pulita. Gli effetti esterni alla transazione, come i valori delle sequence, non vengono annullati.

Le istruzioni che modificano i dati (INSERT, UPDATE, DELETE, MERGE, CTE che modificano dati…) vengono analizzate solo dopo una conferma. Il plugin legge l'istruzione con un lexer che segue le regole di PostgreSQL per stringhe, commenti e dollar quote, e rifiuta input che contengono più di un'istruzione.

Sulle connessioni in sola lettura Beekeeper Studio non consente il ROLLBACK, quindi per le istruzioni che modificano i dati è possibile solo l'explain, ed Explain Analyze non parte mai automaticamente. Una SELECT che chiama una funzione che scrive non viene rilevata, esattamente come quando la esegui in una scheda query.

Il menu Options attiva o disattiva BUFFERS, VERBOSE, SETTINGS e WAL. Le opzioni non supportate dal server sono disabilitate. Le istruzioni con parametri $1 vengono spiegate con GENERIC_PLAN su PostgreSQL 16+.

MySQL e MariaDB

Il plugin legge la versione del server e usa il formato di piano più recente disponibile:

Server Explain Explain Analyze
MySQL 8.0.18+ EXPLAIN FORMAT=TREE EXPLAIN ANALYZE
MySQL da 5.6 a 8.0.17 EXPLAIN FORMAT=JSON Non disponibile
MariaDB 10.1+ EXPLAIN FORMAT=JSON ANALYZE FORMAT=JSON
Server più vecchi La tabella EXPLAIN classica Non disponibile

UPDATE e DELETE su una sola tabella non hanno un tree; il plugin ripiega su FORMAT=JSON e, su MySQL 9, il cui JSON non riesce a mostrarli, sulla tabella classica. TiDB viene riconosciuto e rifiutato invece di essere interpretato male.

Explain Analyze esegue solo istruzioni di lettura. A differenza di PostgreSQL, non si può contare su MySQL per annullare una richiesta fallita: inviata come START TRANSACTION; ...; ROLLBACK;, un'istruzione fallita lascerebbe la transazione aperta su una connessione del pool usata poi da altre schede. Una SELECT che chiama una stored function che scrive esegue il commit di ciò che la funzione ha fatto, come quando la esegui in una scheda query. Su MariaDB, la modalità sola lettura di Beekeeper Studio rifiuta l'istruzione ANALYZE, quindi Explain Analyze non è disponibile sulle connessioni in sola lettura.

L'istruzione viene letta con le regole di MySQL: commenti # e -- che terminano solo a un line feed, escape con backslash, stringhe "..." e commenti /*! ... */, il cui contenuto viene eseguito da MySQL. NO_BACKSLASH_ESCAPES e ANSI_QUOTES cambiano il modo in cui terminano le stringhe, e una connessione del pool può avere l'una o l'altra, quindi un'istruzione deve risultare un'unica istruzione, e di lettura, con ogni loro combinazione. Il testo che un server può interpretare in più modi viene rifiutato invece di tirare a indovinare: un commento /*!50700 ... */ legato alla versione o /*M! ... */ solo per MariaDB, un carriage return dentro un commento --, o un carattere NUL.

MariaDB misura il tempo per ogni tabella anziché per ogni passaggio, quindi il tempo di una join o di una tabella temporanea è quanto la query ha impiegato oltre alle tabelle lette.

SQLite

EXPLAIN QUERY PLAN mostra quali tabelle vengono lette, in che ordine e con quale indice. SQLite non riporta costi, conteggi di righe o tempi, quindi il diagramma mostra i passaggi e la vista a tabella i loro dettagli, e non c'è Explain Analyze. I suggerimenti segnalano gli indici temporanei che SQLite crea a ogni esecuzione, le tabelle scansionate una volta per ogni riga di una join o di una subquery correlata, e gli ordinamenti in un B-tree temporaneo. Le connessioni LibSQL usano lo stesso motore.

Beekeeper Studio divide il testo SQLite in istruzioni con il proprio parser prima di eseguirle, quindi un'istruzione con un punto e virgola al suo interno (in una stringa, un nome o un commento) viene rifiutata: nessuno splitter può allora trovarci una seconda istruzione.

Screenshot

Explain Analyze, con il passaggio lento selezionato e un suggerimento al riguardo:

Piano analizzato con una scansione sequenziale selezionata e il relativo suggerimento

Lo stesso piano come tabella, ordinato per tempo proprio:

Vista a tabella ordinata per tempo proprio

Una query con una CTE, un InitPlan e un SubPlan, ognuno mostrato come ramo separato:

Piano con rami CTE, InitPlan e SubPlan

Il plugin segue il tema di Beekeeper Studio:

Un piano con il tema chiaro

Sviluppo

Richiede Node.js 22+ e 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, che crea un symlink di questa cartella nella directory dei plugin di Beekeeper Studio.

Per impostazione predefinita Beekeeper Studio carica solo i propri plugin in allow-list. Per caricare questo, aggiungi quanto segue al tuo file di configurazione utente e riavvia l'app:

[pluginSystem]
disabled = false

Poi connettiti a un database: Visualize Explain Plan compare nel menu contestuale dell'editor di query ed Explain Visualizer nel menu nuova scheda.

Harness

dev/harness.html fa le veci di Beekeeper Studio: carica il plugin in un iframe e risponde alle sue richieste con gli stessi messaggi usati dall'app, eseguendo query su database reali. È il modo più rapido per lavorare sulla UI, provare tutti i modi in cui la scheda può essere aperta, cambiare database e cambiare 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 punta il MariaDB dell'harness a un altro server (altrimenti usa MYSQL_URL). SQLite non richiede nulla: un database temporaneo viene popolato da scripts/fixtures/seed-sqlite.sql al primo utilizzo, oppure SQLITE_PATH ne indica uno.

Test

  • tests/postgres analizza piani reali catturati da PostgreSQL 12, 17 e 18 (tests/fixtures/postgres). Ogni fixture contiene lo stesso piano in formato JSON e testo, e i test verificano che entrambi producano lo stesso albero.
  • tests/mysql analizza piani catturati da MySQL 5.7, 8.0.17, 8.0, 8.4 e 9.7 e MariaDB 10.4, 10.6 e 11.4 (tests/fixtures/mysql). Ogni fixture contiene tutti i formati che il suo server offre per un'istruzione: tree, EXPLAIN ANALYZE, entrambe le versioni JSON, l'ANALYZE di MariaDB e la tabella classica. I test verificano che i formati concordino.
  • tests/sqlite analizza le righe di EXPLAIN QUERY PLAN da SQLite 3.16 (il formato precedente alla 3.24), 3.27 e 3.45 (tests/fixtures/sqlite).
  • tests/integration esegue EXPLAIN su server attivi: PostgreSQL quando è impostato PG_URL, MySQL o MariaDB quando è impostato MYSQL_URL, e SQLite sempre, tramite il modulo integrato di Node node:sqlite.
  • tests/state copre il comportamento della scheda con un host fittizio.

Per catturare fixture da un'altra versione del server, popolalo ed esegui il suo script di cattura:

# 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

Come funziona

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

Ogni adapter converte l'output di EXPLAIN del proprio database nel modello Plan in src/core/plan.ts. Un PlanNode ha un nome di operazione, un kind (scan, index, join, filter, sort…), stime opzionali (totalCost, estimatedRows), misurazioni opzionali (actualRows, loops, tempi), i valori derivati inclusiveTime, exclusiveTime ed exclusiveCost, oltre a condizioni e proprietà raggruppate per il pannello dei dettagli. Ogni metrica è opzionale e la UI mostra solo ciò che un motore fornisce: un piano SQLite non ha numeri, quindi la tabella mostra i dettagli di ogni passaggio al posto dei costi.

Il tempo proprio dipende dal motore, quindi lo calcola l'adapter. Le regole di PostgreSQL stanno in src/engines/postgres/timing.ts: i tempi per loop vengono moltiplicati per i loop, i worker paralleli vengono contati una sola volta, il tempo delle CTE viene attribuito alle scansioni CTE che le leggono e il tempo di un InitPlan al nodo che lo valuta. Anche MySQL riporta i tempi per loop e usa il computeLoopTimings condiviso (src/core/metrics.ts), con le subquery nella select list aggiunte sopra la query, che non le include. MariaDB misura tabelle e query intere, non passaggi; computeMixedTimings completa il resto.

I tre formati di MySQL vengono letti con gli stessi nomi di operazione: il testo tree e il JSON versione 2 elencano gli stessi iteratori, e il JSON a query block (e la tabella classica, ricostruita in esso) diventa una catena di join nell'ordine delle tabelle. Le regole dei suggerimenti lavorano su questi nomi, non sull'output nativo.

src/core/analysis.ts aggiunge ciò che tutti i motori condividono: quote di tempo e costo, i passaggi più lenti e i suggerimenti sugli errori di stima delle righe.

Aggiungere un database

Non serve modificare nulla al di fuori della cartella del motore e del registry.

  1. Crea src/engines/<name>/ con un oggetto che implementa ExplainEngine (src/engines/types.ts):
    • connectionTypes: i tipi di connessione di Beekeeper Studio che gestisce, e products quando ne copre diversi (MySQL e MariaDB).
    • inspect(sql, server): divide e classifica l'istruzione (modifica i dati?). src/engines/shared/lexer.ts accetta un dialetto: stili di commento, virgolette, placeholder.
    • analyzePolicy(statement, context): se Explain Analyze può eseguirla, cosa chiedere prima e cosa mostrare sotto l'editor. src/engines/shared/analyzePolicy.ts contiene le policy di rollback e di sola lettura.
    • explain(request, runQuery): invia l'SQL di cui il database ha bisogno e analizza il risultato. Può chiamare runQuery più volte, ad esempio per ripiegare su un formato più vecchio.
    • loadServerInfo(runQuery): la versione e qualsiasi altra cosa il motore debba sapere (l'SQL mode di MySQL). Estendi ServerInfo con i tuoi campi.
    • canParse / parse: leggono l'output incollato e i valori dalla griglia dei risultati. Il testo grezzo restituito da un parse deve produrre di nuovo lo stesso piano: le schede ripristinate vengono ricostruite da esso.
    • options: opzioni mostrate nel menu Options, ciascuna con unavailable(server) quando richiede un server più recente.
  2. Mappa gli operatori nativi sui NodeKind e compila le metriche disponibili. Usa gli helper in src/core/metrics.ts per costi e tempi quando servono, e chiama finishPlan() da src/core/analysis.ts alla fine.
  3. Registralo in src/engines/registry.ts, aggiungi fixture catturate da server reali (uno script di cattura in scripts/fixtures/) e test come gli altri.

Note per SQL Server, che è pianificato:

  • Il client SQL Server di Beekeeper Studio divide una richiesta su ; e può eseguire ogni istruzione su una connessione del pool diversa. SET SHOWPLAN_XML ON deve stare da solo nel suo batch e si applica al batch successivo sulla stessa connessione, quindi un piano stimato richiede una nuova API host che esegua i batch su una sola connessione.
  • SET STATISTICS XML ON con l'istruzione in un unico batch (senza ;) fornisce il piano effettivo, ma esegue l'istruzione e restituisce le sue righe al plugin.
  • L'XML del piano incollato da SSMS (.sqlplan) non richiede supporto dall'host.

Rilascio

  1. Esegui bin/make-release.sh. Suggerisce la prossima versione in base ai tag esistenti, imposta version in manifest.json e package.json, fa il commit e pubblica un tag corrispondente come v0.1.0.
  2. Il workflow di rilascio compila, testa e crea una bozza di release su GitHub con manifest.json e explain-visualizer-<version>.zip. Pubblica la bozza.

yarn build && yarn package crea lo stesso archivio in locale.

Licenza

GPL-3.0. Vedi LICENSE.