Surveillance des performances des requĂȘtes PostgreSQL. Partie 1 — rapport

IngĂ©nieur — du latin, cela signifie inspirĂ©.
L'ingénieur peut tout faire. (c) R.Diesel.
Épigraphes.
Surveillance des performances des requĂȘtes PostgreSQL. Partie 1 — rapport
Ou l'histoire de la raison pour laquelle un administrateur de base de données devrait se souvenir de son passé de programmeur.

Préface

Tous les noms ont été modifiés. Les coïncidences sont fortuites. Le matériel représente uniquement l'opinion personnelle de l'auteur.

Avertissement sur les garanties : Il n'y aura pas de description dĂ©taillĂ©e et prĂ©cise des tables et scripts utilisĂ©s dans le cycle d'articles prĂ©vu. Les matĂ©riaux ne pourront pas ĂȘtre utilisĂ©s immĂ©diatement 'EN L'ÉTAT'.
Tout d'abord, en raison du volume important de matériel,
deuxiÚmement, en raison de l'adéquation avec la base de production du client réel.
C'est pourquoi les articles ne présenteront que des idées et des descriptions de maniÚre trÚs générale.
Peut-ĂȘtre qu'Ă  l'avenir, le systĂšme Ă©voluera vers le partage sur GitHub, ou peut-ĂȘtre pas. Le temps le dira.

Début de l'histoire - «Tu te souviens comment tout a commencé ?».
Ce qui en est sorti, en termes trÚs généraux - «La synthÚse comme l'une des méthodes d'amélioration de la performance de PostgreSQL»

Pourquoi tout cela m'intéresse-t-il ?

Eh bien, d'abord pour ne pas oublier, en me rappelant les bons jours Ă  la retraite.
DeuxiĂšmement, pour systĂ©matiser ce qui a Ă©tĂ© Ă©crit. Car parfois, je commence moi-mĂȘme Ă  me perdre et Ă  oublier certaines parties.

Et surtout, qui sait, cela pourrait ĂȘtre utile Ă  quelqu'un et l'aider Ă  ne pas rĂ©inventer la roue ou Ă  ne pas se manger les doigts. En d'autres termes, amĂ©liorer son karma (pas au sens Habr). Car, ce qui est le plus prĂ©cieux dans ce monde, ce sont les idĂ©es. L'essentiel est de trouver une idĂ©e. La concrĂ©tiser est une question purement technique.

Alors, commençons doucement


ÉnoncĂ© du problĂšme.

Il y a :

Base de données PostgreSQL (10.5), de type de charge mixte (OLTP+DSS), de charge moyenne à faible, située dans le cloud AWS.
La surveillance de la base de données est absente, la surveillance de l'infrastructure est assurée par les outils standards d'AWS dans une configuration minimale.

Exigences :

Surveiller les performances et l'Ă©tat de la base de donnĂ©es, trouver et disposer d'informations initiales pour optimiser les requĂȘtes lourdes Ă  la base de donnĂ©es.

Introduction ou analyse des options de solution

Pour commencer, essayons d'examiner les options de solution du point de vue d'une analyse comparative des avantages et des dĂ©sagrĂ©ments pour l'ingĂ©nieur, tandis que les bĂ©nĂ©fices et les pertes pour la direction peuvent ĂȘtre traitĂ©s par ceux qui sont responsables selon l'organigramme.

Option 1 - «Travailler à la demande»

Nous laissons tout comme c'est. Si le client n'est pas satisfait de la performance de la base de données ou de l'application, il informera les ingénieurs DBA par e-mail ou en créant un incident dans le systÚme de tickets.
L'ingĂ©nieur, aprĂšs avoir reçu l'alerte, se penchera sur le problĂšme, proposera une solution ou reportera le problĂšme, espĂ©rant que tout se rĂ©soudra de lui-mĂȘme, et de toute façon, tout sera bientĂŽt oubliĂ©.
GĂąteaux et beignets, bleus et bossesGĂąteaux et beignets :
1. Il n'est pas nécessaire de faire quoi que ce soit de superflu.
2. Il y a toujours la possibilité de se défiler et de feinter.
3. Une montagne de temps que l'on peut utiliser Ă  sa guise.
Bleus et bosses :
1. TĂŽt ou tard, le client se posera des questions sur la nature de l'existence et la justice universelle dans ce monde, et se demandera encore : pourquoi paye-t-il de l'argent ? Les consĂ©quences sont toujours les mĂȘmes — la seule question est quand le client s'ennuiera et se rĂ©signera Ă  dire au revoir. Et le gagne-pain se videra. C'est triste.
2. Le dĂ©veloppement de l'ingĂ©nieur — zĂ©ro.
3. Difficultés dans la planification du travail et de la charge.

Option 2 - «On danse avec des tambourins, on vend et on chausse.»

Point 1-Pourquoi avons-nous besoin d'un systÚme de surveillance, nous traiterons toutes les demandes. Nous exécutons une multitude de demandes vers le dictionnaire de données et les vues dynamiques, activons divers compteurs, compilons tout dans des tableaux, et analysons périodiquement les listes et tableaux. En fin de compte, nous avons de beaux graphiques ou pas trÚs beaux, des tableaux, des rapports. L'essentiel est d'avoir toujours plus.
Point 2-Nous gĂ©nĂ©rons de l'activitĂ© — nous lançons l'analyse de tout cela.
Point 3-Nous prĂ©parons un document, que nous appelons simplement — «comment organiser notre base de donnĂ©es».
Point 4-Le client, voyant toute cette magnificence de graphiques et de chiffres, est dans une naĂŻve confiance enfantine — voilĂ , maintenant tout va fonctionner, bientĂŽt. Et il se sĂ©pare facilement et sans douleur de ses ressources financiĂšres. La direction est aussi convaincue — nos ingĂ©nieurs travaillent d'arrache-pied. La charge est Ă  son maximum.
Point 5-Répéter réguliÚrement le Point 1.
GĂąteaux et beignets, bleus et bossesGĂąteaux et beignets :
1. La vie des managers et des ingénieurs est simple, prévisible et pleine d'activité. Tout bourdonne, tout le monde est occupé.
2. La vie du client n'est pas mal non plus — il est toujours sĂ»r qu'il suffit de patienter un peu et tout se mettra en place. Ça ne s'arrange pas, eh bien, que faire — c'est un monde injuste, peut-ĂȘtre dans une prochaine vie — il aura de la chance.
Bleus et bosses :
1. TĂŽt ou tard, il y aura un fournisseur plus rapide offrant un service similaire, qui fera la mĂȘme chose, mais Ă  un prix lĂ©gĂšrement infĂ©rieur. Et si le rĂ©sultat est le mĂȘme, pourquoi payer plus ? Ce qui, encore une fois, entraĂźnera la disparition de cette source de revenus.
2. C'est ennuyeux. Tout aussi ennuyeux que n'importe quelle activité peu significative.
3. Comme dans l'option prĂ©cĂ©dente, il n'y a pas de dĂ©veloppement. Mais pour un ingĂ©nieur, un inconvĂ©nient est que, contrairement Ă  la premiĂšre option, ici il faut constamment gĂ©nĂ©rer des bases de donnĂ©es d'informations. Et cela prend du temps. Un temps qui pourrait ĂȘtre utilisĂ© de maniĂšre bĂ©nĂ©fique pour soi-mĂȘme. Car si on ne prend pas soin de soi, personne ne le fera.

Option 3 - Il n'est pas nécessaire de réinventer la roue, il faut l'acheter et rouler.

Les ingénieurs d'autres entreprises ne mangent pas de pizza en buvant de la biÚre pour rien (ah, les bons vieux temps de Saint-Pétersbourg des années 90). Utilisons des systÚmes de surveillance qui sont déjà conçus, testés et fonctionnent, et qui apportent en fait un bénéfice (au moins à leurs créateurs).
GĂąteaux et beignets, bleus et bossesGĂąteaux et beignets :
1. Il n'est pas nécessaire de perdre du temps à créer ce qui a déjà été créé. Prenez et utilisez.
2. Les systÚmes de surveillance ne sont pas conçus par des idiots et ils sont bien sûr utiles.
3. Les systÚmes de surveillance fonctionnels fournissent généralement des informations filtrées pertinentes.
Bleus et bosses :
1. Dans ce cas particulier, l'ingénieur n'est pas un ingénieur, mais simplement un utilisateur d'un produit d'autrui. Ou un utilisateur.
2. Il faut convaincre le client de la nécessité d'acheter quelque chose qu'il ne veut, en réalité, pas comprendre ni devoir ; et en plus, le budget annuel a été approuvé et ne changera pas. Ensuite, il faut allouer des ressources distinctes et configurer pour un systÚme particulier. Donc, d'abord il faut payer, payer et encore payer. Et le client est avare. C'est la norme de cette vie.

Que faire - Tchernychevski ? Ta question est tout Ă  fait pertinente. (c)

Dans ce cas prĂ©cis et la situation actuelle, on peut agir un peu diffĂ©remment — et pourquoi ne pas crĂ©er notre propre systĂšme de surveillance.
Surveillance des performances des requĂȘtes PostgreSQL. Partie 1 — rapport
Eh bien, pas vraiment un systĂšme au sens plein du terme, c'est trop ambitieux et prĂ©somptueux, mais au moins simplifier la tĂąche et recueillir plus d'informations pour rĂ©soudre les incidents de performance. Afin de ne pas se retrouver dans une situation oĂč il faut dire : « Va lĂ  oĂč je ne sais pas oĂč, trouve ce que je ne sais pas quoi ».

Quels sont les avantages et les inconvénients de cette option :

Avantages :
1. C'est intéressant. Enfin, au moins plus intéressant que les incessantes « shrink datafile, alter tablespace, etc. »
2. Ce sont de nouvelles compétences et un nouveau développement. Cela donnera tÎt ou tard des récompenses bien méritées.
Inconvénients :
1. Il faudra travailler. Travailler beaucoup.
2. Il faudra réguliÚrement expliquer le sens et les perspectives de toute l'activité.
3. Il faudra cĂ©der Ă  quelque chose, car la seule ressource dont dispose l'ingĂ©nieur — le temps — est limitĂ©e par l'univers.
4. Ce qui est le plus terrible et le plus dĂ©sagrĂ©able — c'est qu'on peut finir par obtenir quelque chose comme « Ni souriceau, ni grenouille, mais une bĂȘte inconnue ».

Qui ne risque rien n'a rien.
Alors — le plus intĂ©ressant commence.

L'idĂ©e gĂ©nĂ©rale — schĂ©matiquement

Surveillance des performances des requĂȘtes PostgreSQL. Partie 1 — rapport
(L'illustration est tirée de l'article «La synthÚse comme l'une des méthodes d'amélioration de la performance de PostgreSQL»)

Explication :

  • Dans la base cible, l'extension standard PostgreSQL — « pg_stat_statements » — est installĂ©e.
  • Dans la base de donnĂ©es de surveillance, nous crĂ©ons un ensemble de tables de service pour stocker l'historique de pg_stat_statements Ă  ses dĂ©buts et pour configurer les mĂ©triques et la surveillance par la suite.
  • Sur l'hĂŽte de surveillance, nous crĂ©ons un ensemble de scripts bash, y compris pour gĂ©nĂ©rer des incidents dans le systĂšme de tickets.

Tables de service

Pour commencer, voici un ERD schématique simplifié de ce qui a été réalisé :
Surveillance des performances des requĂȘtes PostgreSQL. Partie 1 — rapport
Description succincte des tablesendpoint — hîte, point de connexion à l'instance
database — paramĂštres de la base de donnĂ©es
pg_stat_history — table historique pour stocker des instantanĂ©s temporels de la vue pg_stat_statements de la base de donnĂ©es cible
metric_glossary — glossaire des mĂ©triques de performance
metric_config — configuration des mĂ©triques individuelles
metric — mĂ©trique spĂ©cifique pour la requĂȘte surveillĂ©e
metric_alert_history — historique des alertes de performance
log_query — table de service pour stocker les enregistrements analysĂ©s du fichier journal PostgreSQL chargĂ© depuis AWS
baseline — paramĂštres de la pĂ©riode temporelle utilisĂ©e comme rĂ©fĂ©rence
checkpoint — configuration des mĂ©triques de vĂ©rification de l'Ă©tat de la base de donnĂ©es
checkpoint_alert_history — historique des alertes des mĂ©triques de vĂ©rification de l'Ă©tat de la base de donnĂ©es
pg_stat_db_queries — table de service des requĂȘtes actives
activity_log — table de service du journal d'activitĂ©
trap_oid — table de service de configuration du trap

Étape 1 — nous collectons des informations statistiques sur la performance et obtenons des rapports

La table est utilisée pour stocker les informations statistiques pg_stat_history
Structure de la table pg_stat_history

                                          Table "public.pg_stat_history"
       Column        |            Type             |                          Modifiers
---------------------+-----------------------------+-------------------------------------------
 id                  | integer                     | not null default nextval('pg_stat_history_id_seq'::regclass)
 snapshot_timestamp  | timestamp without time zone |
 database_id         | integer                     |
 dbid                | oid                         |
 userid              | oid                         |
 queryid             | bigint                      |
 query               | text                        |
 calls               | bigint                      |
 total_time          | double precision            |
 min_time            | double precision            |
 max_time            | double precision            |
 mean_time           | double precision            |
 stddev_time         | double precision            |
 rows                | bigint                      |
 shared_blks_hit     | bigint                      |
 shared_blks_read    | bigint                      |
 shared_blks_dirtied | bigint                      |
 shared_blks_written | bigint                      |
 local_blks_hit      | bigint                      |
 local_blks_read     | bigint                      |
 local_blks_dirtied  | bigint                      |
 local_blks_written  | bigint                      |
 temp_blks_read      | bigint                      |
 temp_blks_written   | bigint                      |
 blk_read_time       | double precision            |
 blk_write_time      | double precision            |
 baseline_id         | integer                     |
Indexes:
    "pg_stat_history_pkey" PRIMARY KEY, btree (id)
    "database_idx" btree (database_id)
    "queryid_idx" btree (queryid)
    "snapshot_timestamp_idx" btree (snapshot_timestamp)
Foreign-key constraints:
    "database_id_fk" FOREIGN KEY (database_id) REFERENCES database(id) ON DELETE CASCADE

Comme on peut le voir, la table représente simplement des données cumulatives de la vue pg_stat_statements dans la base de données cible.

L'utilisation de cette table est trĂšs simple

pg_stat_history reprĂ©sentera des statistiques cumulĂ©es sur l'exĂ©cution des requĂȘtes pour chaque heure. Au dĂ©but de chaque heure, aprĂšs le remplissage de la table, les statistiques pg_stat_statements sont rĂ©initialisĂ©es Ă  l'aide de pg_stat_statements_reset().
Remarque : les statistiques sont collectĂ©es pour les requĂȘtes ayant une durĂ©e d'exĂ©cution supĂ©rieure Ă  1 seconde.
Remplissage de la table pg_stat_history

--pg_stat_history.sql
CREATE OR REPLACE FUNCTION pg_stat_history( ) RETURNS boolean AS $$
DECLARE
  endpoint_rec record ;
  database_rec record ;
  pg_stat_snapshot record ;
  current_snapshot_timestamp timestamp without time zone;
BEGIN
  current_snapshot_timestamp = date_trunc('minute',now());  
  
  FOR endpoint_rec IN SELECT * FROM endpoint 
  LOOP
    FOR database_rec IN SELECT * FROM database WHERE endpoint_id = endpoint_rec.id 
	  LOOP
	    
		RAISE NOTICE 'NOUVEAU DOSSIER EN COURS DE CRÉATION';
		
		--Se connecter à la base de données cible	  
	    EXECUTE 'SELECT dblink_connect(''LINK1'',''host='||endpoint_rec.host||' dbname='||database_rec.name||' user=USER password=PASSWORD '')';
 
        RAISE NOTICE 'hĂŽte % et dbname % ',endpoint_rec.host,database_rec.name;
		RAISE NOTICE 'CrĂ©ation d’un instantanĂ© de pg_stat_statements pour la base de donnĂ©es %',database_rec.name;
		
		SELECT 
	      *
		INTO 
		  pg_stat_snapshot
	    FROM dblink('LINK1',
	      'SELECT 
	       dbid , SUM(calls),SUM(total_time),SUM(rows) ,SUM(shared_blks_hit) ,SUM(shared_blks_read) ,SUM(shared_blks_dirtied) ,SUM(shared_blks_written) , 
           SUM(local_blks_hit) , SUM(local_blks_read) , SUM(local_blks_dirtied) , SUM(local_blks_written) , SUM(temp_blks_read) , SUM(temp_blks_written) , SUM(blk_read_time) , SUM(blk_write_time)
	       FROM pg_stat_statements WHERE dbid=(SELECT oid from pg_database where datname=current_database() ) 
		   GROUP BY dbid
  	      '
	               )
	      AS t
	       ( dbid oid , calls bigint , 
  	         total_time double precision , 
	         rows bigint , shared_blks_hit bigint , shared_blks_read bigint ,shared_blks_dirtied bigint ,shared_blks_written	 bigint ,
             local_blks_hit	 bigint ,local_blks_read bigint , local_blks_dirtied bigint ,local_blks_written bigint ,
             temp_blks_read	 bigint ,temp_blks_written bigint ,
             blk_read_time double precision , blk_write_time double precision	  
	       );
		 
		INSERT INTO pg_stat_history
          ( 
		    snapshot_timestamp  ,database_id  ,
			dbid , calls  ,total_time ,
            rows ,shared_blks_hit  ,shared_blks_read  ,shared_blks_dirtied  ,shared_blks_written ,local_blks_hit , 	 	
            local_blks_read,local_blks_dirtied,local_blks_written,temp_blks_read,temp_blks_written, 	
            blk_read_time, blk_write_time 
		  )		  
	    VALUES
	      (
	       current_snapshot_timestamp ,
		   database_rec.id ,
	       pg_stat_snapshot.dbid ,pg_stat_snapshot.calls,
	       pg_stat_snapshot.total_time,
	       pg_stat_snapshot.rows ,pg_stat_snapshot.shared_blks_hit ,pg_stat_snapshot.shared_blks_read ,pg_stat_snapshot.shared_blks_dirtied ,pg_stat_snapshot.shared_blks_written , 
           pg_stat_snapshot.local_blks_hit , pg_stat_snapshot.local_blks_read , pg_stat_snapshot.local_blks_dirtied , pg_stat_snapshot.local_blks_written , 
	       pg_stat_snapshot.temp_blks_read , pg_stat_snapshot.temp_blks_written , pg_stat_snapshot.blk_read_time , pg_stat_snapshot.blk_write_time 	   
	      );		   
		  
        RAISE NOTICE 'CrĂ©ation d’un instantanĂ© de pg_stat_statements pour les requĂȘtes ayant un temps minimum supĂ©rieur Ă  1000ms';
	
        FOR pg_stat_snapshot IN
          --Toutes les requĂȘtes avec un temps_max supĂ©rieur Ă  1000 ms
	      SELECT 
	        *
	      FROM dblink('LINK1',
	        'SELECT 
	         dbid , userid ,queryid,query,calls,total_time,min_time ,max_time,mean_time, stddev_time ,rows ,shared_blks_hit ,
			 shared_blks_read ,shared_blks_dirtied ,shared_blks_written , 
             local_blks_hit , local_blks_read , local_blks_dirtied , 
			 local_blks_written , temp_blks_read , temp_blks_written , blk_read_time , 
			 blk_write_time
	         FROM pg_stat_statements 
			 WHERE dbid=(SELECT oid from pg_database where datname=current_database() AND min_time >= 1000 ) 
  	        '

	                  )
	        AS t
	         ( dbid oid , userid oid , queryid bigint ,query text , calls bigint , 
  	           total_time double precision ,min_time double precision	 ,max_time double precision	 , mean_time double precision	 ,  stddev_time double precision	 , 
	           rows bigint , shared_blks_hit bigint , shared_blks_read bigint ,shared_blks_dirtied bigint ,shared_blks_written	 bigint ,
               local_blks_hit	 bigint ,local_blks_read bigint , local_blks_dirtied bigint ,local_blks_written bigint ,
               temp_blks_read	 bigint ,temp_blks_written bigint ,
               blk_read_time double precision , blk_write_time double precision	  
	         )
	    LOOP
		  INSERT INTO pg_stat_history
          ( 
		    snapshot_timestamp  ,database_id  ,
			dbid ,userid  , queryid  , query  , calls  ,total_time ,min_time ,max_time ,mean_time ,stddev_time ,
            rows ,shared_blks_hit  ,shared_blks_read  ,shared_blks_dirtied  ,shared_blks_written ,local_blks_hit , 	 	
            local_blks_read,local_blks_dirtied,local_blks_written,temp_blks_read,temp_blks_written, 	
            blk_read_time, blk_write_time 
		  )		  
	      VALUES
	      (
	       current_snapshot_timestamp ,
		   database_rec.id ,
	       pg_stat_snapshot.dbid ,pg_stat_snapshot.userid ,pg_stat_snapshot.queryid,pg_stat_snapshot.query,pg_stat_snapshot.calls,
	       pg_stat_snapshot.total_time,pg_stat_snapshot.min_time ,pg_stat_snapshot.max_time,pg_stat_snapshot.mean_time, pg_stat_snapshot.stddev_time ,
	       pg_stat_snapshot.rows ,pg_stat_snapshot.shared_blks_hit ,pg_stat_snapshot.shared_blks_read ,pg_stat_snapshot.shared_blks_dirtied ,pg_stat_snapshot.shared_blks_written , 
           pg_stat_snapshot.local_blks_hit , pg_stat_snapshot.local_blks_read , pg_stat_snapshot.local_blks_dirtied , pg_stat_snapshot.local_blks_written , 
	       pg_stat_snapshot.temp_blks_read , pg_stat_snapshot.temp_blks_written , pg_stat_snapshot.blk_read_time , pg_stat_snapshot.blk_write_time 	   
	      );
		  
        END LOOP;

        PERFORM dblink_disconnect('LINK1');  
				
	  END LOOP ;--FOR database_rec IN SELECT * FROM database WHERE endpoint_id = endpoint_rec.id 
    
  END LOOP;

RETURN TRUE;  
END
$$ LANGUAGE plpgsql;

En conséquence, aprÚs un certain laps de temps dans le tableau pg_stat_history nous aurons un ensemble de captures du contenu du tableau pg_stat_statements de la base de données cible.

En fait, le reporting

En utilisant des requĂȘtes simples, on peut obtenir des rapports assez utiles et intĂ©ressants.

Données agrégées sur une période donnée

RequĂȘte

SELECT 
  database_id , 
  SUM(calls) AS calls ,SUM(total_time)  AS total_time ,
  SUM(rows) AS rows , SUM(shared_blks_hit)  AS shared_blks_hit,
  SUM(shared_blks_read) AS shared_blks_read ,
  SUM(shared_blks_dirtied) AS shared_blks_dirtied,
  SUM(shared_blks_written) AS shared_blks_written , 
  SUM(local_blks_hit) AS local_blks_hit , 
  SUM(local_blks_read) AS local_blks_read , 
  SUM(local_blks_dirtied) AS local_blks_dirtied , 
  SUM(local_blks_written)  AS local_blks_written,
  SUM(temp_blks_read) AS temp_blks_read, 
  SUM(temp_blks_written) temp_blks_written , 
  SUM(blk_read_time) AS blk_read_time , 
  SUM(blk_write_time) AS blk_write_time
FROM 
  pg_stat_history
WHERE 
  queryid IS NULL AND
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
GROUP BY database_id ;

Temps DB

to_char(interval '1 millisecond' * pg_total_stat_history_rec.total_time, 'HH24:MI:SS.MS')

Temps I/O

to_char(interval '1 millisecond' * ( pg_total_stat_history_rec.blk_read_time + pg_total_stat_history_rec.blk_write_time ), 'HH24:MI:SS.MS')

TOP10 SQL par total_time

RequĂȘte

SELECT 
  queryid , 
  SUM(calls) AS calls ,
  SUM(total_time)  AS total_time  	
FROM 
  pg_stat_history
WHERE 
  queryid IS NOT NULL AND 
  database_id = DATABASE_ID AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT 
GROUP BY queryid 
ORDER BY 3 DESC 
LIMIT 10
-------------------------------------------------------------------------------------
| TOP10 SQL PAR TEMPS D'EXÉCUTION TOTAL
|   #|    queryid|      appels|    % des appels|                total_time (ms) |  % dbtime
+----+-----------+-----------+-----------+--------------------------------+----------
|   1|  821760255|          2|     .00001|00:03:23.141(    203141.681 ms.)|      5.42
|   2| 4152624390|          2|     .00001|00:03:13.929(    193929.215 ms.)|      5.17
|   3| 1484454471|          4|     .00001|00:02:09.129(    129129.057 ms.)|      3.44
|   4|  655729273|          1|     .00000|00:02:01.869(    121869.981 ms.)|      3.25
|   5| 2460318461|          1|     .00000|00:01:33.113(     93113.835 ms.)|      2.48
|   6| 2194493487|          4|     .00001|00:00:17.377(     17377.868 ms.)|       .46
|   7| 1053044345|          1|     .00000|00:00:06.156(      6156.352 ms.)|       .16
|   8| 3644780286|          1|     .00000|00:00:01.063(      1063.830 ms.)|       .03

TOP10 SQL par temps I/O total

RequĂȘte

SELECT 
  queryid , 
  SUM(calls) AS calls ,
  SUM(blk_read_time + blk_write_time)  AS io_time
FROM 
  pg_stat_history
WHERE 
  queryid IS NOT NULL AND 
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
GROUP BY  queryid 
ORDER BY 3 DESC 
LIMIT 10
----------------------------------------------------------------------------------------
| TOP10 SQL PAR TEMPS I/O TOTAL
|   #|    identifiant de requĂȘte|      appels|    % d'appels|                   temps I/O (ms)|% temps I/O db
+----+-----------+-----------+-----------+--------------------------------+-------------
|   1| 4152624390|          2|     .00001|00:08:31.616(    511616.592 ms.)|        31.06
|   2|  821760255|          2|     .00001|00:08:27.099(    507099.036 ms.)|        30.78
|   3|  655729273|          1|     .00000|00:05:02.209(    302209.137 ms.)|        18.35
|   4| 2460318461|          1|     .00000|00:04:05.981(    245981.117 ms.)|        14.93
|   5| 1484454471|          4|     .00001|00:00:39.144(     39144.221 ms.)|         2.38
|   6| 2194493487|          4|     .00001|00:00:18.182(     18182.816 ms.)|         1.10
|   7| 1053044345|          1|     .00000|00:00:16.611(     16611.722 ms.)|         1.01
|   8| 3644780286|          1|     .00000|00:00:00.436(       436.205 ms.)|          .03

TOP10 SQL par le temps d'exécution maximum

RequĂȘte

SELECT 
  id AS snapshotid , 
  queryid , 
  snapshot_timestamp ,  
  max_time 
FROM 
  pg_stat_history 
WHERE 
  queryid IS NOT NULL AND 
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
ORDER BY 4 DESC 
LIMIT 10

-----------------------------------------------------------------------------------------
| TOP10 SQL PAR TEMPS D'EXÉCUTION MAXIMAL
|   #|          instantanĂ©| identifiant de l'instantanĂ©|    identifiant de requĂȘte|                           temps_max (ms)
+----+------------------+-----------+-----------+----------------------------------------
|   1|  05.04.2019 01:03|       4169|  655729273|        00:02:01.869(    121869.981 ms.)
|   2|  04.04.2019 17:00|       4153|  821760255|        00:01:41.570(    101570.841 ms.)
|   3|  04.04.2019 16:00|       4146|  821760255|        00:01:41.570(    101570.841 ms.)
|   4|  04.04.2019 16:00|       4144| 4152624390|        00:01:36.964(     96964.607 ms.)
|   5|  04.04.2019 17:00|       4151| 4152624390|        00:01:36.964(     96964.607 ms.)
|   6|  05.04.2019 10:00|       4188| 1484454471|        00:01:33.452(     93452.150 ms.)
|   7|  04.04.2019 17:00|       4150| 2460318461|        00:01:33.113(     93113.835 ms.)
|   8|  04.04.2019 15:00|       4140| 1484454471|        00:00:11.892(     11892.302 ms.)
|   9|  04.04.2019 16:00|       4145| 1484454471|        00:00:11.892(     11892.302 ms.)
|  10|  04.04.2019 17:00|       4152| 1484454471|        00:00:11.892(     11892.302 ms.)

TOP10 SQL par la lecture/Ă©criture de tampon PARTAGÉ

RequĂȘte

SELECT 
  id AS snapshotid , 
  queryid ,
  snapshot_timestamp , 
  shared_blks_read , 
  shared_blks_written 
FROM 
  pg_stat_history
WHERE 
  queryid IS NOT NULL AND 
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT AND
  ( shared_blks_read > 0 OR shared_blks_written > 0 )
ORDER BY 4 DESC  , 5 DESC 
LIMIT 10
--------------------------------------------------------------------------------------------
| TOP10 SQL PAR LECTURE/ÉCRITURE DU BUFFER PARTAGÉ
|   #|          instantané| snapshotID|    queryid|   blocs partagés lus|  blocs partagés écrits
+----+------------------+-----------+-----------+---------------------+---------------------
|   1|  04.04.2019 17:00|       4153|  821760255|               797308|                    0
|   2|  04.04.2019 16:00|       4146|  821760255|               797308|                    0
|   3|  05.04.2019 01:03|       4169|  655729273|               797158|                    0
|   4|  04.04.2019 16:00|       4144| 4152624390|               756514|                    0
|   5|  04.04.2019 17:00|       4151| 4152624390|               756514|                    0
|   6|  04.04.2019 17:00|       4150| 2460318461|               734117|                    0
|   7|  04.04.2019 17:00|       4155| 3644780286|                52973|                    0
|   8|  05.04.2019 01:03|       4168| 1053044345|                52818|                    0
|   9|  04.04.2019 15:00|       4141| 2194493487|                52813|                    0
|  10|  04.04.2019 16:00|       4147| 2194493487|                52813|                    0
--------------------------------------------------------------------------------------------

Histogramme de la rĂ©partition des requĂȘtes par temps d'exĂ©cution maximal

RequĂȘtes

SELECT  
  MIN(max_time) AS hist_min  , 
  MAX(max_time) AS hist_max , 
  (( MAX(max_time) - MIN(min_time) ) / hist_columns ) as hist_width
FROM 
  pg_stat_history 
WHERE 
  queryid IS NOT NULL AND
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT ;

SELECT 
  SUM(calls) AS calls
FROM 
  pg_stat_history 
WHERE 
  queryid IS NOT NULL AND
  database_id =DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT AND 
  ( max_time >= hist_current_min AND  max_time < hist_current_max ) ;
|-----------------------------------------------------------------------------------------------
| HISTOGRAMME DU TEMPS MAXIMAL
| APPELS TOTAUX : 33851920
| TEMPS MIN  : 00:00:01.063
| TEMPS MAX  : 00:02:01.869
---------------------------------------------------------------------------------
|                      durée min|                      durée max|     appels
+----------------------------------+----------------------------------+----------
| 00:00:01.063(      1063.830 ms.) | 00:00:13.144(     13144.445 ms.) | 9
| 00:00:13.144(     13144.445 ms.) | 00:00:25.225(     25225.060 ms.) | 0
| 00:00:25.225(     25225.060 ms.) | 00:00:37.305(     37305.675 ms.) | 0
| 00:00:37.305(     37305.675 ms.) | 00:00:49.386(     49386.290 ms.) | 0
| 00:00:49.386(     49386.290 ms.) | 00:01:01.466(     61466.906 ms.) | 0
| 00:01:01.466(     61466.906 ms.) | 00:01:13.547(     73547.521 ms.) | 0
| 00:01:13.547(     73547.521 ms.) | 00:01:25.628(     85628.136 ms.) | 0
| 00:01:25.628(     85628.136 ms.) | 00:01:37.708(     97708.751 ms.) | 4
| 00:01:37.708(     97708.751 ms.) | 00:01:49.789(    109789.366 ms.) | 2
| 00:01:49.789(    109789.366 ms.) | 00:02:01.869(    121869.981 ms.) | 0

TOP10 InstantanĂ©s par RequĂȘte par Seconde

RequĂȘtes

--pg_qps.sql
--Calculer le nombre de requĂȘtes par seconde 
CREATE OR REPLACE FUNCTION pg_qps( pg_stat_history_id integer ) RETURNS double precision AS $$
DECLARE
 pg_stat_history_rec record ;
 prev_pg_stat_history_id integer ;
 prev_pg_stat_history_rec record;
 total_seconds double precision ;
 result double precision;
BEGIN 
  result = 0 ;
  
  SELECT *
  INTO pg_stat_history_rec
  FROM 
    pg_stat_history
  WHERE id = pg_stat_history_id ;

  IF pg_stat_history_rec.snapshot_timestamp IS NULL 
  THEN
    RAISE EXCEPTION 'ERREUR - pg_stat_history non trouvé pour id = %',pg_stat_history_id;
  END IF ;  
  
 --RAISE NOTICE 'pg_stat_history_id = % , snapshot_timestamp = %', pg_stat_history_id , 
 pg_stat_history_rec.snapshot_timestamp ;
  
  SELECT 
    MAX(id)   
  INTO
    prev_pg_stat_history_id
  FROM
    pg_stat_history
  WHERE 
    database_id = pg_stat_history_rec.database_id AND
	queryid IS NULL AND
	id  0 
  THEN
    result = pg_stat_history_rec.calls / total_seconds ;
  ELSE
   result = 0 ; 
  END IF;
   
 RETURN result ;
END
$$ LANGUAGE plpgsql;


SELECT 
  id , 
  snapshot_timestamp ,
  calls , 	
  total_time , 
  ( select pg_qps( id )) AS QPS ,
  blk_read_time ,
  blk_write_time
FROM 
  pg_stat_history
WHERE 
  queryid IS NULL AND 
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT AND
  ( select pg_qps( id )) IS NOT NULL 
ORDER BY 5 DESC 
LIMIT 10
|-----------------------------------------------------------------------------------------------
| TOP10 Snapshots classés par le nombre de QueryPerSeconds
-----------------------------------------------------------------------------------------------------------------------------------------------
|    #|          snapshot| snapshotID|      appels|                      temps total db|        QPS|                          temps I/O| Pourcentage temps I/O
+-----+------------------+-----------+-----------+----------------------------------+-----------+----------------------------------+-----------
|    1|  04.04.2019 20:04|       4161|    5758631|  00:06:30.513(    390513.926 ms.)|   1573.396|  00:00:01.470(      1470.110 ms.)|       .376
|    2|  04.04.2019 17:00|       4149|    3529197|  00:11:48.830(    708830.618 ms.)|    980.332|  00:12:47.834(    767834.052 ms.)|    108.324
|    3|  04.04.2019 16:00|       4143|    3525360|  00:10:13.492(    613492.351 ms.)|    979.267|  00:08:41.396(    521396.555 ms.)|     84.988
|    4|  04.04.2019 21:03|       4163|    2781536|  00:03:06.470(    186470.979 ms.)|    785.745|  00:00:00.249(       249.865 ms.)|       .134
|    5|  04.04.2019 19:03|       4159|    2890362|  00:03:16.784(    196784.755 ms.)|    776.979|  00:00:01.441(      1441.386 ms.)|       .732
|    6|  04.04.2019 14:00|       4137|    2397326|  00:04:43.033(    283033.854 ms.)|    665.924|  00:00:00.024(        24.505 ms.)|       .009
|    7|  04.04.2019 15:00|       4139|    2394416|  00:04:51.435(    291435.010 ms.)|    665.116|  00:00:12.025(     12025.895 ms.)|      4.126
|    8|  04.04.2019 13:00|       4135|    2373043|  00:04:26.791(    266791.988 ms.)|    659.179|  00:00:00.064(        64.261 ms.)|       .024
|    9|  05.04.2019 01:03|       4167|    4387191|  00:06:51.380(    411380.293 ms.)|    609.332|  00:05:18.847(    318847.407 ms.)|     77.507
|   10|  04.04.2019 18:01|       4157|    1145596|  00:01:19.217(     79217.372 ms.)|    313.004|  00:00:01.319(      1319.676 ms.)|      1.666

Historique d'exécution horaire avec QueryPerSeconds et Temps I/O

RequĂȘte

SELECT 
  id , 
  snapshot_timestamp ,
  appels , 	
  temps_total , 
  ( select pg_qps( id )) AS QPS ,
  temps_blk_lecture ,
  temps_blk_ecriture
FROM 
  pg_stat_history
WHERE 
  queryid IS NULL AND 
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
ORDER BY 2
|-----------------------------------------------------------------------------------------------
| HISTORIQUE D'EXÉCUTION HORAIRE AVEC QueryPerSeconds et Temps I/O
-----------------------------------------------------------------------------------------------------------------------------------------------
| HISTORIQUE DES REQUÊTES PAR SECONDE
|    #|          instantané| snapshotID|      appels|                      temps total db|        QPS|                          temps I/O| % temps I/O
+-----+------------------+-----------+-----------+----------------------------------+-----------+----------------------------------+-----------
|    1|  04.04.2019 11:00|       4131|       3747|  00:00:00.835(       835.374 ms.)|      1.041|  00:00:00.000(          .000 ms.)|       .000
|    2|  04.04.2019 12:00|       4133|    1002722|  00:01:52.419(    112419.376 ms.)|    278.534|  00:00:00.149(       149.105 ms.)|       .133
|    3|  04.04.2019 13:00|       4135|    2373043|  00:04:26.791(    266791.988 ms.)|    659.179|  00:00:00.064(        64.261 ms.)|       .024
|    4|  04.04.2019 14:00|       4137|    2397326|  00:04:43.033(    283033.854 ms.)|    665.924|  00:00:00.024(        24.505 ms.)|       .009
|    5|  04.04.2019 15:00|       4139|    2394416|  00:04:51.435(    291435.010 ms.)|    665.116|  00:00:12.025(     12025.895 ms.)|      4.126
|    6|  04.04.2019 16:00|       4143|    3525360|  00:10:13.492(    613492.351 ms.)|    979.267|  00:08:41.396(    521396.555 ms.)|     84.988
|    7|  04.04.2019 17:00|       4149|    3529197|  00:11:48.830(    708830.618 ms.)|    980.332|  00:12:47.834(    767834.052 ms.)|    108.324
|    8|  04.04.2019 18:01|       4157|    1145596|  00:01:19.217(     79217.372 ms.)|    313.004|  00:00:01.319(      1319.676 ms.)|      1.666
|    9|  04.04.2019 19:03|       4159|    2890362|  00:03:16.784(    196784.755 ms.)|    776.979|  00:00:01.441(      1441.386 ms.)|       .732
|   10|  04.04.2019 20:04|       4161|    5758631|  00:06:30.513(    390513.926 ms.)|   1573.396|  00:00:01.470(      1470.110 ms.)|       .376
|   11|  04.04.2019 21:03|       4163|    2781536|  00:03:06.470(    186470.979 ms.)|    785.745|  00:00:00.249(       249.865 ms.)|       .134
|   12|  04.04.2019 23:03|       4165|    1443155|  00:01:34.467(     94467.539 ms.)|    200.438|  00:00:00.015(        15.287 ms.)|       .016
|   13|  05.04.2019 01:03|       4167|    4387191|  00:06:51.380(    411380.293 ms.)|    609.332|  00:05:18.847(    318847.407 ms.)|     77.507
|   14|  05.04.2019 02:03|       4171|     189852|  00:00:10.989(     10989.899 ms.)|     52.737|  00:00:00.539(       539.110 ms.)|      4.906
|   15|  05.04.2019 03:01|       4173|       3627|  00:00:00.103(       103.000 ms.)|      1.042|  00:00:00.004(         4.131 ms.)|      4.010
|   16|  05.04.2019 04:00|       4175|       3627|  00:00:00.085(        85.235 ms.)|      1.025|  00:00:00.003(         3.811 ms.)|      4.471
|   17|  05.04.2019 05:00|       4177|       3747|  00:00:00.849(       849.454 ms.)|      1.041|  00:00:00.006(         6.124 ms.)|       .721
|   18|  05.04.2019 06:00|       4179|       3747|  00:00:00.849(       849.561 ms.)|      1.041|  00:00:00.000(          .051 ms.)|       .006
|   19|  05.04.2019 07:00|       4181|       3747|  00:00:00.839(       839.416 ms.)|      1.041|  00:00:00.000(          .062 ms.)|       .007
|   20|  05.04.2019 08:00|       4183|       3747|  00:00:00.846(       846.382 ms.)|      1.041|  00:00:00.000(          .007 ms.)|       .001
|   21|  05.04.2019 09:00|       4185|       3747|  00:00:00.855(       855.426 ms.)|      1.041|  00:00:00.000(          .065 ms.)|       .008
|   22|  05.04.2019 10:00|       4187|       3797|  00:01:40.150(    100150.165 ms.)|      1.055|  00:00:21.845(     21845.217 ms.)|     21.812

Texte de tous les SQL-selects

RequĂȘte

SELECT 
  queryid , 
  query 
FROM 
  pg_stat_history
WHERE 
  queryid IS NOT NULL AND 
  database_id = DATABASE_ID  AND
  snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
GROUP BY queryid , query

Conclusion

Comme on peut le voir, avec des moyens assez simples, il est possible d'obtenir beaucoup d'informations utiles sur la charge et l'état de la base de données.

Remarque :Si nous enregistrons le queryid dans les requĂȘtes, nous obtiendrons un historique pour chaque requĂȘte (dans un souci d'Ă©conomie d'espace, les rapports pour chaque requĂȘte sont omis).

Ainsi, les donnĂ©es statistiques sur les performances des requĂȘtes sont disponibles et collectĂ©es.
La premiÚre étape, « collecte de données statistiques », est terminée.

Nous pouvons passer à la deuxiÚme étape : « configuration des métriques de performance ».
Surveillance des performances des requĂȘtes PostgreSQL. Partie 1 — rapport

Mais c'est déjà une toute autre histoire.

À suivre...

Source : habr.com

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster