Saurez-vous sauver cette instance ?
Luc LAMARLE, Mathieu RIBES, Frédéric YHUEL
14 janvier 2025
Les VM hébergeant les instances PostgreSQL disposent de 8 Go de RAM et 4 CPU.
Afin de faciliter la mise en place de cet atelier, les paramètres
suivants ont été mis en place dans le fichier
postgresql.conf
. Ils ne doivent pas être modifiés.
log_line_prefix = '%t [%p]: user=%u,db=%d,app=%a,client=%h '
log_checkpoints = on
log_connections = on
log_disconnections = on
log_lock_waits = on
log_temp_files = 0
log_autovacuum_min_duration = 0
log_error_verbosity = default
log_min_duration_statement = 0
lc_messages = 'C'
max_parallel_workers_per_gather = 0
jit = off
Vérifier les paramètres de bases de l’instance et les corriger si nécessaire. Voici une liste non exhaustive de paramètres importants à vérifier :
SELECT * FROM users u
JOIN votes v ON (u.id = v.userid) AND v.votetypeid = 5
JOIN votes v2 ON (u.id = v2.userid) AND v2.votetypeid = 6
JOIN votes v3 ON (u.id = v3.userid) AND v3.votetypeid = 8
JOIN votes v4 ON (u.id = v4.userid) AND v4.votetypeid = 10
JOIN votes v5 ON (u.id = v5.userid) AND v5.votetypeid = 4;
Le champ creationdate
est de type
timestamp without time zone
et aucun index n’est créé sur
celui-ci. Nous allons donc le créer pour voir si cela améliore le
comportement :
SELECT COUNT(ph.posthistorytypeid) AS nb, pht.name AS name
FROM posthistory ph JOIN posthistorytypes pht ON (ph.posthistorytypeid = pht.id)
WHERE ph.creationdate < '2011-01-01' GROUP BY 2;
Rien à signaler sur la table posthistorytypes
, il y a
bien un index sur le champ id
car c’est une clé primaire.
En revanche, sur la table posthistory
, aucun index n’est
présent sur le champ creationdate
.
Voyons si la création de celui-ci change quelque chose :
Le temps de réponse est encore élevé, regardons s’il n’est pas possible de faire mieux.
interaction_interac_idx
pour voir son niveau de
fragmentation (bloat
).La requête suivante est disponible pour avoir une estimation du
niveau de bloat des index : requête
bloat. Cette requête nécessite d’être superuser
et
d’avoir des statistiques à jour.
interaction_interac_idx
Le parcours de l’index est-il plus rapide ?
Aucun index n’est créé sur le champ closeddate
.
Pas d’amélioration obtenu avec cet index.
id'
avec une clause
sur le champ closeddate
.Cette requête effectue une recherche sur le motif pi%
en
utlisant un Seq Scan
.
Le champ title
est de type text
et n’est
pas indexé. Créer l’index suivant.
Aucun changement, l’index n’est même pas utilisé.
Le gain est énorme. L’index est cette fois bien utilisé.
Il y a bien un index sur le champ creationdate
.
Cependant, en regardant de plus près on remarque le mot clé
INVALID
qui indique que celui-ci n’est pas utilisable. Ce
problème peut aparaitre notamment lors des opérations de création
d’index ou de réindexation avec la clause CONCURRENTLY
.
La requête suivante permet de remonter l’ensemble des index invalides d’une base de données :
SELECT indrelid::regclass, indexrelid::regclass FROM pg_index WHERE indisvalid ='f';
indrelid | indexrelid
----------+---------------------------
comments | comments_creationdate_idx
On y retrouve bien l’index comments_creationdate_idx
sur
la table comments
.
Aucun index sur le champ jsonfield
. Est-ce qu’un index
classique sur ce champ permettrait d’améliorer la requête ?
SELECT u.displayname AS name, max(c.creationdate) AS date_last_comment
FROM comments c JOIN users u ON (c.userid = u.id)
WHERE userid = 664306 GROUP BY 1;
Le principal problème ici vient du Seq Scan
réalisé sur
la table comments
pour répondre à la clause
WHERE
. Seulement 6 lignes sont retournées et 5745561 sont
supprimées par le clause WHERE
.
Aucun index n’est présent sur le champ userid
. La source
de nos lenteurs vient donc à priori de là.
SELECT DISTINCT location, age, displayname
FROM users
WHERE creationdate > '2021-01-01'
ORDER BY 1, 2;
Un index est bien utilisé pour la clause WHERE
et il n’y
a pas de jointure. La piste d’un index manquant est donc à écarter.
explain (analyze,buffers) SELECT DISTINCT location, age, displayname
FROM users
WHERE creationdate > '2021-01-01'
ORDER BY 1, 2;
On peut constater que des données sont écrites sur disques :
Disk: 2192kB
. Cette écriture est provoquée par la clause
ORDER BY
de la requête.
La requête réalise ici un Seq Scan
et filtre énormement
de lignes pour peu de résultats retournés. La piste d’un index manquant
ou non utilisé est donc à prioriser.
Un index est bien présent, mais non utilisé.
Nous sommes ici dans un cas similaire à la requête 6. L’utilisation
du mot clé LIKE
dans la requête rend inutilisable un index
utilisant la classe d’opérateur définit par défaut.
Aucun index n’est présent sur le champ location
. Il faut
donc en créer un mais en modifiant la classe d’opérateur pour qu’il
puisse être utilisé par la clause LIKE
.
Aucun index sur le champ age
. Comme pour les requêtes 2
et 11, la clause WHERE
filtre sur le résultat d’une
fonction.