menu
languageΕλληνικά
save_alt Λήψη

Explain Visualizer

Οπτικοποίησε τα πλάνα εκτέλεσης query ως διαδραστικό διάγραμμα και βρες τα αργά σημεία ενός query.

από rathboma Plugin της κοινότητας

Readme

Αυτό το κείμενο γράφτηκε από τρίτους και μεταφράστηκε αυτόματα. Διάβασε το πρωτότυπο στα αγγλικά

Ένα plugin του Beekeeper Studio που μετατρέπει τα πλάνα εκτέλεσης query σε διαδραστικό διάγραμμα, ώστε να βρίσκεις εύκολα το αργό σημείο ενός query.

Παρουσίαση: δεξί κλικ σε ένα query, άνοιγμα του πλάνου του, εκτέλεση Explain Analyze και μετάβαση στο αργό βήμα

  • Διάγραμμα του πλάνου, χρωματισμένο ανάλογα με το μερίδιο κάθε βήματος στον χρόνο εκτέλεσης (ή στο εκτιμώμενο κόστος). Οι φαρδύτερες συνδέσεις μεταφέρουν περισσότερες γραμμές.
  • Προβολή πίνακα στο στυλ του explain.depesz.com, με ταξινόμηση κατά ίδιο χρόνο.
  • Λεπτομέρειες για κάθε βήμα: ίδιος και συνολικός χρόνος, πραγματικές έναντι εκτιμώμενων γραμμών, loops, κόστη, συνθήκες, buffers, χρήση μνήμης και δίσκου, workers.
  • Insights που επισημαίνουν συνηθισμένα προβλήματα: πλήρεις σαρώσεις που απορρίπτουν τις περισσότερες γραμμές, πίνακες που σαρώνονται μία φορά ανά γραμμή ενός join, sorts και hashes που χύνονται στον δίσκο, εξαρτημένα subqueries, προσωρινά indexes που χτίζει το SQLite σε κάθε εκτέλεση, μεγάλες λάθος εκτιμήσεις γραμμών και άλλα.
  • Ακατέργαστη έξοδος του πλάνου, έτοιμη για αντιγραφή ή αποθήκευση.

Περισσότερα στα στιγμιότυπα παρακάτω.

Υποστηριζόμενες βάσεις δεδομένων

Βάση δεδομένων Explain Explain Analyze Επικόλληση πλάνου
PostgreSQL Ναι Ναι JSON και κείμενο psql
MySQL Ναι 8.0.18+, εντολές ανάγνωσης Tree, JSON (και οι δύο εκδόσεις) και ο κλασικός πίνακας
MariaDB Ναι 10.1+, εντολές ανάγνωσης JSON και ο κλασικός πίνακας
SQLite, LibSQL Ναι Όχι: το SQLite δεν έχει χρονομετρήσεις βημάτων Το δέντρο του shell sqlite3 ή οι γραμμές αποτελεσμάτων
SQL Server Προγραμματισμένο    

Δοκιμασμένο με PostgreSQL 12, 17 και 18· MySQL 5.7, 8.0.17, 8.0, 8.4 και 9.7· MariaDB 10.4, 10.6 και 11.4· και SQLite 3.16, 3.27 και 3.45. Το plugin βασίζεται σε ένα μοντέλο πλάνου ανεξάρτητο από τη βάση δεδομένων, οπότε κάθε βάση χρειάζεται μόνο έναν adapter (δες Προσθήκη βάσης δεδομένων).

Χρήση

Υπάρχουν διάφοροι τρόποι:

  • Επεξεργαστής query: κάνε δεξί κλικ σε ένα query και διάλεξε Visualize Explain Plan. Χρησιμοποιείται το επιλεγμένο κείμενο ή η εντολή κάτω από τον κέρσορα.
  • Μενού νέας καρτέλας: άνοιξε το Explain Visualizer, γράψε μια εντολή και διάλεξε Explain ή Explain Analyze.
  • Πλέγμα αποτελεσμάτων: αφού τρέξεις μόνος σου το EXPLAIN, κάνε δεξί κλικ στο πλάνο και διάλεξε Visualize Explain Plan:
    • πλάνο σε ένα κελί (FORMAT JSON της PostgreSQL, tree ή JSON της MySQL): δεξί κλικ στο κελί·
    • πλάνο σε πολλές γραμμές (έξοδος κειμένου της PostgreSQL): δεξί κλικ στην κεφαλίδα της στήλης QUERY PLAN·
    • πίνακας (κλασικό EXPLAIN της MySQL, EXPLAIN QUERY PLAN του SQLite): επίλεξε όλες τις γραμμές και στήλες του και κάνε δεξί κλικ στην επιλογή. Για το SQLite αρκεί και μόνο η στήλη detail, χωρίς την ένθεση.
  • Paste Plan: επικόλλησε έξοδο EXPLAIN, για παράδειγμα από το psql, το shell mysql ή sqlite3, ή από έναν συνάδελφο.

Το μενού περιβάλλοντος του επεξεργαστή query με το Visualize Explain Plan

Explain και Explain Analyze

Το Explain ζητά από τη βάση δεδομένων το πλάνο της χωρίς να εκτελέσει την εντολή. Είναι πάντα ασφαλές, γι' αυτό τρέχει αυτόματα όταν η καρτέλα ανοίγει από τον επεξεργαστή.

Το Explain Analyze εκτελεί την εντολή και μετρά κάθε βήμα. Το πώς διατηρείται ασφαλές εξαρτάται από τη βάση δεδομένων, δες παρακάτω. Αν το κείμενο του επεξεργαστή ξεκινά ήδη με EXPLAIN ANALYZE (ή το αντίστοιχο της βάσης), το plugin το χρησιμοποιεί· όταν η εντολή δεν μπορεί να αναλυθεί, εκτελείται απλό explain και ο λόγος εμφανίζεται κάτω από τον επεξεργαστή.

PostgreSQL

Το plugin στέλνει EXPLAIN ANALYZE ...; ROLLBACK; ως ένα αίτημα, ώστε και οι δύο εντολές να μοιράζονται μία implicit συναλλαγή και ό,τι άλλαξε η εντολή να αναιρείται. Αυτό ισχύει και για απλά SELECT, που μπορούν να καλούν functions που γράφουν. Αν η εντολή αποτύχει, η συναλλαγή ακυρώνεται από μόνη της και η pooled σύνδεση μένει καθαρή. Επιδράσεις εκτός της συναλλαγής, όπως τιμές sequence, δεν αναιρούνται.

Οι εντολές που αλλάζουν δεδομένα (INSERT, UPDATE, DELETE, MERGE, CTE που τροποποιούν δεδομένα…) αναλύονται μόνο μετά από επιβεβαίωση. Το plugin διαβάζει την εντολή με έναν lexer που ακολουθεί τους κανόνες της ίδιας της PostgreSQL για strings, σχόλια και dollar quotes, και απορρίπτει είσοδο που περιέχει περισσότερες από μία εντολές.

Σε συνδέσεις μόνο ανάγνωσης το Beekeeper Studio δεν επιτρέπει το ROLLBACK, οπότε οι εντολές που αλλάζουν δεδομένα μπορούν μόνο να γίνουν explain και το Explain Analyze δεν τρέχει ποτέ αυτόματα. Ένα SELECT που καλεί function που γράφει δεν εντοπίζεται εκεί, όπως ακριβώς και όταν το τρέχεις σε καρτέλα query.

Το μενού Options ενεργοποιεί/απενεργοποιεί τα BUFFERS, VERBOSE, SETTINGS και WAL. Οι επιλογές που δεν υποστηρίζει ο server είναι απενεργοποιημένες. Οι εντολές με παραμέτρους $1 γίνονται explain με GENERIC_PLAN σε PostgreSQL 16+.

MySQL και MariaDB

Το plugin διαβάζει την έκδοση του server και χρησιμοποιεί τη νεότερη μορφή πλάνου που διαθέτει:

Server Explain Explain Analyze
MySQL 8.0.18+ EXPLAIN FORMAT=TREE EXPLAIN ANALYZE
MySQL 5.6 έως 8.0.17 EXPLAIN FORMAT=JSON Μη διαθέσιμο
MariaDB 10.1+ EXPLAIN FORMAT=JSON ANALYZE FORMAT=JSON
Παλαιότεροι servers Ο κλασικός πίνακας EXPLAIN Μη διαθέσιμο

Τα UPDATE και DELETE ενός πίνακα δεν έχουν tree· το plugin καταφεύγει στο FORMAT=JSON και, στη MySQL 9, της οποίας το JSON δεν μπορεί να τα δείξει ούτε αυτό, στον κλασικό πίνακα. Το TiDB αναγνωρίζεται και απορρίπτεται αντί να διαβαστεί λάθος.

Το Explain Analyze εκτελεί μόνο εντολές ανάγνωσης. Σε αντίθεση με την PostgreSQL, δεν μπορείς να βασιστείς στη MySQL για να αναιρέσει ένα αποτυχημένο αίτημα: αν σταλεί ως START TRANSACTION; ...; ROLLBACK;, μια εντολή που απέτυχε θα άφηνε τη συναλλαγή ανοιχτή σε μια pooled σύνδεση που χρησιμοποιούν μετά άλλες καρτέλες. Ένα SELECT που καλεί stored function που γράφει κάνει commit ό,τι έκανε η function, όπως ακριβώς όταν το τρέχεις σε καρτέλα query. Στη MariaDB, η λειτουργία μόνο ανάγνωσης του Beekeeper Studio απορρίπτει την εντολή ANALYZE, οπότε το Explain Analyze δεν είναι διαθέσιμο σε συνδέσεις μόνο ανάγνωσης.

Η εντολή διαβάζεται με τους κανόνες της MySQL: σχόλια # και -- που τελειώνουν μόνο σε line feed, backslash escapes, strings "..." και σχόλια /*! ... */, των οποίων το περιεχόμενο εκτελεί η MySQL. Τα NO_BACKSLASH_ESCAPES και ANSI_QUOTES αλλάζουν το πού τελειώνουν τα strings, και μια pooled σύνδεση μπορεί να έχει οποιοδήποτε από τα δύο, οπότε μια εντολή πρέπει να διαβάζεται ως μία εντολή, και ως ανάγνωση, σε κάθε συνδυασμό τους. Κείμενο που ένας server μπορεί να διαβάσει με περισσότερους από έναν τρόπους απορρίπτεται αντί να γίνει εικασία: ένα σχόλιο /*!50700 ... */ που εξαρτάται από την έκδοση ή ένα /*M! ... */ μόνο για MariaDB, ένα carriage return μέσα σε σχόλιο -- ή ένας χαρακτήρας NUL.

Η MariaDB χρονομετρά κάθε πίνακα αντί για κάθε βήμα, οπότε ο χρόνος ενός join ή ενός προσωρινού πίνακα είναι ό,τι χρειάστηκε το query πέρα από τους πίνακες που διάβασε.

SQLite

Το EXPLAIN QUERY PLAN δείχνει ποιοι πίνακες διαβάζονται, με ποια σειρά και με ποιο index. Το SQLite δεν αναφέρει κόστη, πλήθος γραμμών ή χρόνους, οπότε το διάγραμμα δείχνει τα βήματα και η προβολή πίνακα τις λεπτομέρειές τους, και δεν υπάρχει Explain Analyze. Τα insights επισημαίνουν προσωρινά indexes που χτίζει το SQLite σε κάθε εκτέλεση, πίνακες που σαρώνονται μία φορά ανά γραμμή ενός join ή correlated subquery, και sorts σε προσωρινό B-tree. Οι συνδέσεις LibSQL χρησιμοποιούν την ίδια μηχανή.

Το Beekeeper Studio χωρίζει το κείμενο SQLite σε εντολές με τον δικό του parser πριν τις εκτελέσει, οπότε μια εντολή με ερωτηματικό μέσα της (σε string, όνομα ή σχόλιο) απορρίπτεται: έτσι κανένας splitter δεν μπορεί να βρει δεύτερη εντολή μέσα της.

Στιγμιότυπα

Explain Analyze, με επιλεγμένο το αργό βήμα και ένα insight γι' αυτό:

Αναλυμένο πλάνο με επιλεγμένο ένα sequential scan και το insight του

Το ίδιο πλάνο ως πίνακας, ταξινομημένο κατά ίδιο χρόνο:

Προβολή πίνακα ταξινομημένη κατά ίδιο χρόνο

Ένα query με CTE, InitPlan και SubPlan, το καθένα ως ξεχωριστός κλάδος:

Πλάνο με κλάδους CTE, InitPlan και SubPlan

Το plugin ακολουθεί το θέμα του Beekeeper Studio:

Ένα πλάνο στο φωτεινό θέμα

Ανάπτυξη

Απαιτεί Node.js 22+ και 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 χρησιμοποιεί το @beekeeperstudio/vite-plugin, που δημιουργεί symlink αυτού του φακέλου στον κατάλογο plugins του Beekeeper Studio.

Από προεπιλογή το Beekeeper Studio φορτώνει μόνο τα δικά του εγκεκριμένα plugins. Για να φορτώσεις αυτό, πρόσθεσε τα παρακάτω στο αρχείο ρυθμίσεων χρήστη και κάνε επανεκκίνηση της εφαρμογής:

[pluginSystem]
disabled = false

Μετά σύνδεσε μια βάση δεδομένων: το Visualize Explain Plan εμφανίζεται στο μενού περιβάλλοντος του επεξεργαστή query και το Explain Visualizer στο μενού νέας καρτέλας.

Harness

Το dev/harness.html παίζει τον ρόλο του Beekeeper Studio: φορτώνει το plugin σε ένα iframe και απαντά στα αιτήματά του με τα ίδια μηνύματα που χρησιμοποιεί η εφαρμογή, εκτελώντας queries σε πραγματικές βάσεις δεδομένων. Είναι ο πιο γρήγορος τρόπος να δουλέψεις στο UI, να δοκιμάσεις κάθε τρόπο ανοίγματος της καρτέλας, να αλλάξεις βάσεις δεδομένων και θέματα.

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 κατευθύνει τη MariaDB του harness σε άλλον server (διαφορετικά χρησιμοποιεί το MYSQL_URL). Το SQLite δεν χρειάζεται τίποτα: μια προσωρινή βάση δεδομένων γεμίζει από το scripts/fixtures/seed-sqlite.sql στην πρώτη χρήση, ή το SQLITE_PATH ορίζει μία.

Tests

  • Το tests/postgres αναλύει πραγματικά πλάνα από PostgreSQL 12, 17 και 18 (tests/fixtures/postgres). Κάθε fixture έχει το ίδιο πλάνο σε μορφή JSON και κειμένου, και τα tests ελέγχουν ότι και τα δύο παράγουν το ίδιο δέντρο.
  • Το tests/mysql αναλύει πλάνα από MySQL 5.7, 8.0.17, 8.0, 8.4 και 9.7 και MariaDB 10.4, 10.6 και 11.4 (tests/fixtures/mysql). Κάθε fixture έχει κάθε μορφή που διαθέτει ο server του για μία εντολή: tree, EXPLAIN ANALYZE, και τις δύο εκδόσεις JSON, το ANALYZE της MariaDB και τον κλασικό πίνακα. Τα tests ελέγχουν ότι οι μορφές συμφωνούν.
  • Το tests/sqlite αναλύει γραμμές EXPLAIN QUERY PLAN από SQLite 3.16 (η μορφή πριν το 3.24), 3.27 και 3.45 (tests/fixtures/sqlite).
  • Το tests/integration εκτελεί EXPLAIN σε ζωντανούς servers: PostgreSQL όταν έχει οριστεί το PG_URL, MySQL ή MariaDB όταν έχει οριστεί το MYSQL_URL, και SQLite πάντα, μέσω του ενσωματωμένου node:sqlite του Node.
  • Το tests/state καλύπτει τη συμπεριφορά της καρτέλας με έναν ψεύτικο host.

Για να καταγράψεις fixtures από άλλη έκδοση server, γέμισέ τον με δεδομένα και τρέξε το script καταγραφής του:

# 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

Πώς λειτουργεί

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

Κάθε adapter μετατρέπει την έξοδο EXPLAIN της βάσης του στο μοντέλο Plan στο src/core/plan.ts. Ένα PlanNode έχει όνομα λειτουργίας, ένα kind (scan, index, join, filter, sort…), προαιρετικές εκτιμήσεις (totalCost, estimatedRows), προαιρετικές μετρήσεις (actualRows, loops, χρόνους), παράγωγα inclusiveTime, exclusiveTime και exclusiveCost, καθώς και συνθήκες και ομαδοποιημένες ιδιότητες για το πάνελ λεπτομερειών. Κάθε μετρική είναι προαιρετική και το UI δείχνει μόνο ό,τι παρέχει η μηχανή: ένα πλάνο SQLite δεν έχει αριθμούς, οπότε ο πίνακας δείχνει τις λεπτομέρειες κάθε βήματος αντί για κόστη.

Ο ίδιος χρόνος εξαρτάται από τη μηχανή, γι' αυτό τον υπολογίζει ο adapter. Οι κανόνες της PostgreSQL βρίσκονται στο src/engines/postgres/timing.ts: οι χρόνοι ανά loop πολλαπλασιάζονται με τα loops, οι parallel workers μετριούνται μία φορά, ο χρόνος των CTE χρεώνεται στα CTE scans που τα διαβάζουν και ο χρόνος ενός InitPlan χρεώνεται στον κόμβο που το αποτιμά. Η MySQL αναφέρει επίσης χρόνους ανά loop και χρησιμοποιεί το κοινό computeLoopTimings (src/core/metrics.ts), με τα subqueries της λίστας select να προστίθενται πάνω από το query, που δεν τα περιλαμβάνει. Η MariaDB χρονομετρά πίνακες και ολόκληρα queries, όχι βήματα· το computeMixedTimings συμπληρώνει τα υπόλοιπα.

Οι τρεις μορφές της MySQL διαβάζονται στα ίδια ονόματα λειτουργιών: το κείμενο tree και το JSON έκδοσης 2 απαριθμούν τους ίδιους iterators, και το query-block JSON (και ο κλασικός πίνακας, που ανακατασκευάζεται σε αυτό) γίνεται αλυσίδα από joins με τη σειρά των πινάκων. Οι κανόνες των insights δουλεύουν πάνω σε αυτά τα ονόματα, όχι στην εγγενή έξοδο.

Το src/core/analysis.ts προσθέτει ό,τι είναι κοινό σε όλες τις μηχανές: μερίδια χρόνου και κόστους, τα πιο αργά βήματα και insights για λάθος εκτιμήσεις γραμμών.

Προσθήκη βάσης δεδομένων

Δεν χρειάζεται να αλλάξει τίποτα εκτός από τον φάκελο της μηχανής και το registry.

  1. Δημιούργησε το src/engines/<name>/ με ένα αντικείμενο που υλοποιεί το ExplainEngine (src/engines/types.ts):
    • connectionTypes: οι τύποι σύνδεσης του Beekeeper Studio που χειρίζεται, και products όταν καλύπτει πολλούς (MySQL και MariaDB).
    • inspect(sql, server): διαχωρίζει και κατηγοριοποιεί την εντολή (αλλάζει δεδομένα;). Το src/engines/shared/lexer.ts δέχεται μια διάλεκτο: στυλ σχολίων, quotes, placeholders.
    • analyzePolicy(statement, context): αν το Explain Analyze μπορεί να την εκτελέσει, τι να ρωτήσει πρώτα και τι να γράψει κάτω από τον επεξεργαστή. Το src/engines/shared/analyzePolicy.ts έχει τις πολιτικές rollback και μόνο ανάγνωσης.
    • explain(request, runQuery): στέλνει όποιο SQL χρειάζεται η βάση δεδομένων και αναλύει το αποτέλεσμα. Μπορεί να καλέσει το runQuery αρκετές φορές, για παράδειγμα για να καταφύγει σε παλαιότερη μορφή.
    • loadServerInfo(runQuery): η έκδοση και οτιδήποτε άλλο χρειάζεται να ξέρει η μηχανή (το SQL mode της MySQL). Επέκτεινε το ServerInfo με τα δικά σου πεδία.
    • canParse / parse: διαβάζουν επικολλημένη έξοδο και τιμές από το πλέγμα αποτελεσμάτων. Το ακατέργαστο κείμενο που επιστρέφει ένα parse πρέπει να αναλύεται ξανά στο ίδιο πλάνο: οι καρτέλες που επαναφέρονται ανακατασκευάζονται από αυτό.
    • options: διακόπτες που εμφανίζονται στο μενού Options, ο καθένας με unavailable(server) όταν χρειάζεται νεότερο server.
  2. Αντιστοίχισε τους εγγενείς τελεστές σε NodeKind και συμπλήρωσε όποιες μετρικές υπάρχουν. Χρησιμοποίησε τα helpers στο src/core/metrics.ts για κόστη και χρόνους όπου ταιριάζουν και κάλεσε το finishPlan() από το src/core/analysis.ts στο τέλος.
  3. Κάνε την εγγραφή στο src/engines/registry.ts, πρόσθεσε fixtures από πραγματικούς servers (ένα script καταγραφής στο scripts/fixtures/) και tests όπως τα υπόλοιπα.

Σημειώσεις για τον SQL Server, που είναι προγραμματισμένος:

  • Ο client SQL Server του Beekeeper Studio χωρίζει ένα αίτημα στο ; και μπορεί να εκτελέσει κάθε εντολή σε διαφορετική pooled σύνδεση. Το SET SHOWPLAN_XML ON πρέπει να είναι μόνο του στο batch του και ισχύει για το επόμενο batch στην ίδια σύνδεση, οπότε ένα εκτιμώμενο πλάνο χρειάζεται νέο host API που εκτελεί batches σε μία σύνδεση.
  • Το SET STATISTICS XML ON με την εντολή σε ένα batch (χωρίς ;) δίνει το πραγματικό πλάνο, αλλά εκτελεί την εντολή και επιστρέφει τις γραμμές της στο plugin.
  • Το XML πλάνου που επικολλάται από το SSMS (.sqlplan) δεν χρειάζεται υποστήριξη από τον host.

Κυκλοφορία εκδόσεων

  1. Τρέξε το bin/make-release.sh. Προτείνει την επόμενη έκδοση από τα υπάρχοντα tags, ορίζει το version στα manifest.json και package.json, κάνει commit και push ένα αντίστοιχο tag όπως v0.1.0.
  2. Το release workflow κάνει build, τρέχει τα tests και δημιουργεί ένα draft GitHub release με τα manifest.json και explain-visualizer-<version>.zip. Δημοσίευσε το draft.

Το yarn build && yarn package δημιουργεί το ίδιο αρχείο τοπικά.

Άδεια

GPL-3.0. Δες το LICENSE.