
Cet outil a Ă©tĂ© Ă©crit par intĂ©rĂȘt sportif lorsque j'ai dĂ©couvert que la vue pg_stat_activity dans PostgreSQL 10 avait les champs wait_event_type et wait_event, qui sont trĂšs similaires en substance Ă wait_class et Ă l'Ă©vĂ©nement de la session v $.
Travaillant activement avec le
programme ASH-Viewer d'
Akardapolov en ce moment, j'Ă©tais curieux de voir Ă quel point il est difficile de réécrire ce produit sous Postgres. Ătant donnĂ© que je ne suis pas un dĂ©veloppeur professionnel, ce n'Ă©tait pas facile, mais trĂšs intĂ©ressant. Dans le processus, j'ai mĂȘme trouvĂ©, il me semble, quelques bugs importants qui apparaissent dans le programme d'origine pour Oracle, ainsi que pour l'Ă©dition standard.
Les principes de PASH-Viewer:
Aucune extension nécessaire. Nous prenons les données exclusivement de la vue pg_stat_activity intégrée.
Une fois par seconde, une demande est faite pour les sessions actives:
pg_stat_activity requĂȘte textSELECT current_timestamp, datname, pid, usesysid, usename, application_name, backend_type, coalesce(client_hostname, client_addr::text, 'localhost') as client_hostname, wait_event_type, wait_event, query, query_start, 1000 * EXTRACT(EPOCH FROM (clock_timestamp()-query_start)) as duration from pg_stat_activity where state='active' and pid != pg_backend_pid();
Toutes les 15 secondes, les données des 15 derniÚres images sont moyennées et affichées sur un graphique.
L'ID SQL qui est nĂ©cessaire pour regrouper les requĂȘtes dans la section Top SQL, je me gĂ©nĂšre moi-mĂȘme, cela n'a rien Ă voir avec l'ID de requĂȘte de pg_stat_statements. Je pensais comment utiliser queryid, mais malheureusement je n'ai pas trouvĂ© de moyen de faire correspondre les requĂȘtes de ces deux vues. Ce serait formidable si les dĂ©veloppeurs ajoutaient le champ queryid Ă pg_stat_activity.
ID SQL = 13 premiers caractĂšres de md5 (texte de requĂȘte normalisĂ©).
Un texte de requĂȘte normalisĂ© est une requĂȘte dans laquelle les caractĂšres de nouvelle ligne et les espaces supplĂ©mentaires sont supprimĂ©s et les littĂ©raux sont remplacĂ©s par $ 1, $ 2, etc ... Il m'a Ă©tĂ© difficile d'Ă©crire une bonne fonction de normalisation de requĂȘte. J'en ai Ă©crit une mauvaise. Je cite le texte, mais ne le regardez pas, sinon j'ai honte. Mieux vaut en envoyer un bon.
NormalizeSQL public static String NormalizeSQL(String sql) { sql = sql.replaceAll("\\n", " "); sql = sql.replaceAll("\\(", " ( "); sql = sql.replaceAll("\\)", " ) "); sql = sql.replaceAll(",", " , "); sql = sql.replaceAll("'", " ' "); sql = sql.replaceAll("=", " = "); sql = sql.replaceAll("<", " < "); sql = sql.replaceAll(">", " > "); sql = sql.replaceAll(";", ""); sql = sql.replaceAll("[ ]+", " "); sql = sql.replaceAll("> =", ">="); sql = sql.replaceAll("< =", "<="); sql = sql.toLowerCase().trim(); String[] array = sql.split(" ", -1); int var_number = 0; String normalized_sql = ""; Boolean quote_flag = false; for (int i = 0; i < array.length; i++) { if (array[i].equals("'")) { if (!quote_flag) { quote_flag = true; var_number++; normalized_sql += "$" + var_number + " "; } else { quote_flag = false; } } else if (quote_flag) { continue; } else if (array[i].matches("-?\\d+(\\.\\d+)?")) { var_number++; normalized_sql += "$" + var_number + " "; } else if (array[i].equals("order")) { for (int j = i; j < array.length; j++) { normalized_sql += array[j] + " "; } return normalized_sql.trim(); } else { normalized_sql += array[i] + " "; } } return normalized_sql.trim(); }
Il Ă©tait difficile de terminer le plan d'exĂ©cution des requĂȘtes. C'est Ă Oracle que vous venez et dites: "Donnez-moi un plan pour sqlid = ...", et il vous rĂ©pond - "Avez-vous le dernier, ou hier, ou montrez tout pour le dernier mois avec des statistiques d'exĂ©cution pour chacun?" Et PostgreSQL vous rĂ©pond - "Qu'est-ce que sqlid?".
Par consĂ©quent, pour les requĂȘtes de la forme SELECT / UPDATE / INSERT / DELETE, nous envoyons la commande EXPLAIN Ă la base de donnĂ©es et enregistrons le rĂ©sultat localement. Nous ne le faisons pas plus d'une fois par heure. Pendant le dĂ©bogage, il a Ă©tĂ© constatĂ© qu'EXPLAIN se bloque sur le verrou de la mĂȘme maniĂšre que la demande elle-mĂȘme se bloquerait, pour laquelle nous voulons connaĂźtre le plan. Par consĂ©quent, j'ai dĂ» ajouter setQueryTimeout (1).
Et cela ne fonctionne que si la demande a Ă©tĂ© exĂ©cutĂ©e dans la mĂȘme base de donnĂ©es Ă laquelle vous vous ĂȘtes connectĂ© (indiquĂ© lors de la configuration de la connexion). Et seulement si vous vous connectez Ă la base de donnĂ©es sous le superutilisateur (postgres), ce dont certains peuvent avoir peur. Par consĂ©quent, vous pouvez crĂ©er un utilisateur spĂ©cial pour la surveillance. Tout sauf l'affichage des plans fonctionnera.
CREATE USER pgmonuser WITH password 'pgmonuser'; GRANT pg_monitor TO pgmonuser;
Téléchargement depuis GitHub:
https://github.com/dbacvetkov/PASH-Viewer/releasesUPD:Dans la version 0.3, il a ajouté la prise en charge de PostgreSQL 9.6 (il n'y a que deux classes en attente - Lock et LWLock, tout le reste va comme «CPU») et PostgreSQL 9.4 - 9.5 (il y a soit un CPU soit un Lock en attente).
Dans la version 0.3.1, ajout du champ Backend Type dans Top Sessions et suppression des barres blanches du graphique.
Dans la version 0.3.2, amélioration du travail avec les plans, ajout de statistiques sur les demandes (durée AVG, nombre d'appels) et possibilité de visualiser les données historiques:
Comment-créer-pg_stat_activity-history-table .
Merci et salutations:
Alexander Kardapolov pour ASH-Viewer.
Anton Glushakov pour consultation et test.
Dmitry Rudopysov pour avoir expliqué comment compiler et exécuter le projet téléchargé depuis github.
Plus de diapositives:


