Comment puis-je résoudre les problèmes d’utilisation intensive du CPU pour Amazon RDS ou Amazon Aurora PostgreSQL ?

Date de la dernière mise à jour : 16/04/2019

Comment puis-je identifier et résoudre la cause de problèmes d’utilisation intensive du CPU dans Amazon Relational Database Service (Amazon RDS) ou Amazon Aurora PostgreSQL ?

Brève description

Si vous voyez que votre charge cause une utilisation intensive du CPU, vous pouvez utiliser une combinaison des outils suivants pour identifier la cause d'une charge du CPU :

  • Les métriques Amazon CloudWatch peuvent vous aider à identifier les modèles d'utilisation du CPU sur une période donnée.
  • Enhanced Monitoring (Surveillance accrue) fournit une vue au niveau du système d'exploitation, ce qui peut vous aider à identifier la cause d'une charge intensive du CPU à un niveau granulaire. Par exemple, vous pouvez consulter la charge moyenne, la distribution du CPU (system% ou nice%) et la liste de traitement du système d'exploitation.
  • Enhanced Monitoring (Surveillance accrue) permet également d'identifier les micro pics d’utilisation du CPU. CloudWatch peut rater les micro pics si le pic d'activité se produit pendant une période de moins de 60 secondes. Enhanced Monitoring (Surveillance accrue) peut identifier ces pics, car vous pouvez configurer la surveillance pour afficher une granularité d’une seconde.
  • Analyse des performances peut vous aider à identifier les requêtes exactes responsables de la charge et l'hôte qui a lancé les requêtes.
  • Affichage et catalogues PostgreSQL natif (notamment pg_stat_statements, pg_stat_activity et pg_stat_user_tables) vous permettent de consulter les détails au niveau de la base de données. Pour plus d'informations, consultez la documentation PostgreSQL relative à la Surveillance de l’activité de la base de données et pg_stat_statements.
  • PostgreSQL fournit une grande variété de paramètres de journalisation pour consigner les requêtes de longue durée, autovacuum, verrouiller les attentes et demandes de connexion et de déconnexion. Pour plus d'informations, consultez Comment activer la journalisation des requêtes à l'aide d'Amazon RDS pour PostgreSQL ?

Une fois que vous avez identifié la cause, vous pouvez utiliser les méthodes suivantes pour réduire l'utilisation du CPU :

  • S'il y a des occasions de réglage, utilisez EXPLAIN (EXPLIQUER) et EXPLAIN ANALYZE (EXPLIQUER L’ANALYSE) pour identifier les mises en garde. Pour plus d'informations, consultez la documentation PostgreSQL relative à EXPLAIN (EXPLIQUER).
  • S'il y a une requête en cours d'exécution répétée, utilisez les instructions préparées pour réduire la pression sur votre CPU.

Résolution

Métriques Amazon CloudWatch

Vous pouvez utiliser les métriques CloudWatch pour identifier les modèles de CPU pendant des périodes prolongées. Vous pouvez comparer la métrique CloudWatch pour identifier le délai pendant lequel l’utilisation du CPU est la plus intensive. Une fois le délai identifié, vous pouvez consulter les données Enhanced Monitoring (Surveillance accrue) associées à votre instance de base de données. Vous pouvez définir Enhanced Monitoring (Surveillance accrue) pour collecter les données à intervalles de 1, 5, 10, 15, 30 ou 60 secondes, ce qui vous permet de collecter les données à un niveau granulaire supérieure à CloudWatch. Pour plus d'informations, consultez la rubrique Différences entre les métriques de CloudWatch et d’Enhanced Monitoring (Surveillance accrue).

Enhanced Monitoring (Surveillance accrue)

En utilisant Enhanced Monitoring (Surveillance accrue), vous pouvez vérifier les données loadAverageMinute par intervalles de 1, 5 et 15 minutes. Si la charge moyenne est supérieure au nombre de vCPU, cela indique que l'instance est sous une lourde charge. En outre, si la charge moyenne est inférieure au nombre de vCPU pour la classe d'instance de base de données, la latence des applications peut ne pas être causées par la limitation du CPU. Vérifiez la charge moyenne pour éviter les fausses erreurs lors du diagnostic de la cause de l’utilisation du CPU.

Par exemple, si vous avez une instance de base de données à l'aide d'une classe d'instance db.m4.2xlarge avec 3 000 IOPS provisionnés qui atteint la limite de CPU, vous pouvez consulter l'exemple suivant de métriques pour identifier la cause racine de l'utilisation intensive du CPU. Dans l'exemple suivant, la classe d'instance dispose de huit vCPU qui lui sont associés. Pour la même classe d’instance, une charge moyenne supérieure à 170 indique que la machine est sous une charge importante au cours de la période mesurée :

Minute de la charge moyenne
 
Quinze 170,25
Cinq 391,31
Un 596,74
Utilisation du CPU  
Utilisateur (%) 0,71
Système (%) 4,9
Nice (%) 93,92
Total (%) 99,97

Remarque : votre charge de travail est prioritaire par rapport à d'autres tâches qui sont en cours d'exécution sur l'instance de base de données. Pour hiérarchiser ces tâches, les tâches de la charge de travail se voient attribuer une valeur Nice supérieure. Par conséquent, dans Enhanced Monitoring (Surveillance accrue), Nice% représente la quantité de CPU utilisée par votre charge de travail par rapport à la base de données.

Après avoir activé Enhanced Monitoring (Surveillance accrue), vous pouvez également vérifier la liste de traitement du système d'exploitation associé à l'instance de base de données. E,ha,ces Monitoring indique uniquement les 50 principaux processus, mais cela peut vous aider à identifier les processus ayant le plus grand impact sur les performances basées sur le CPU et l'utilisation de la mémoire.

Analyse des performances

Vous pouvez utiliser Analyse des performances Amazon RDS pour identifier la requête responsable de la charge de la base de données après avoir vérifié l'onglet SQL qui correspond à une période donnée.

Affichage et catalogues PostgreSQL natif

Au niveau du moteur de base de données, si le problème se produit en temps réel, vous pouvez utiliser pg_stat_activity ou pg_stat_statements. Ceci peut vous aider à regrouper les machines, les clients, et les adresses IP avec le trafic le plus important. Vous pouvez également utiliser ces données pour voir s'il y a des augmentations au fil du temps, les augmentations des serveurs d'application ou si un serveur d'application a des sessions bloquées ou des problèmes de verrouillage. Pour plus d'informations, consultez la documentation PostgreSQL pour pg_stat_activity et pg_stat_statements. Pour activer pg_stat_statements, modifiez le groupe de paramètres personnalisés existants et définissez les valeurs suivantes :

  • shared_preload_libraries = pg_stat_statements
  • track_activity_query_size = 2048
  • pg_stat_statements.track = ALL
  • pg_stat_statements.max = 10000

Choisissez Apply Immediately (Appliquer immédiatement), puis redémarrez l'instance de base de données. Ensuite, exécutez une commande semblable à la commande suivante sur la base de données que vous souhaitez surveiller :

Remarque : l'exemple suivant installe l'extension dans la base de données « démo ».

demo=> select current_database();
current_database
------------------
demo
(1 row)

demo=> CREATE EXTENSION pg_stat_statements;

Une fois pg_stat_statements configuré, vous pouvez surveiller la sortie en utilisant l'une des méthodes suivantes :

  • Liste des requêtes par total_time
  • Répertorier les requêtes avec le nombre total d'appels, le nombre total de lignes et de lignes retournées
  • Liste des requêtes sur une base par exécution

Pour plus d'informations sur les colonnes de l'exemple de requêtes suivant, consultez la documentation PostgreSQL pour pg_stat_statements.

Répertorier des requêtes par total_time et voir quelle requête consacre la plupart du temps dans la base de données :

SELECT round(total_time*1000)/1000 AS total_time,query
FROM pg_stat_statements
ORDER BY total_time DESC limit 5;

Exemple de sortie :

benchmark=> SELECT round(total_time*1000)/1000 AS total_time,query 
FROM pg_stat_statements
ORDER BY total_time DESC limit 5;
-[ RECORD 1 ]-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
total_time | 50795183.089
query      | SELECT MD5((bbalance*random()*bid+?)::char) from pgbench_branches where (MD5((bid*random()*bbalance+?)::char))>substr(?,((random()*(?-?)+?)::integer),?) order by (bbalance*random()*bid+?) desc;
-[ RECORD 2 ]-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
total_time | 37131467.568
query      | UPDATE pgbench_accounts SET abalance = abalance + ? WHERE aid = ?;
-[ RECORD 3 ]-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
total_time | 27716213.981
query      | SELECT abalance FROM pgbench_accounts WHERE aid = ?;
-[ RECORD 4 ]-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
total_time | 25529446.83
query      | UPDATE pgbench_branches SET bbalance = bbalance + ? WHERE bid = ?;
-[ RECORD 5 ]-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
total_time | 5094847.077
query      | UPDATE pgbench_tellers SET tbalance = tbalance + ? WHERE tid = ?;

Répertorier des requêtes avec le nombre total d'appels, le total de lignes et des lignes retournées :

SELECT query, calls, total_time, rows, 100.0 * shared_blks_hit /
nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements ORDER BY total_time DESC LIMIT 5;

Exemple de sortie :

benchmark=> SELECT query, calls, total_time, rows, 100.0 * shared_blks_hit /
nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements ORDER BY total_time DESC LIMIT 5;
-[ RECORD 1 ]--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
query       | SELECT MD5((bbalance*random()*bid+?)::char) from pgbench_branches where (MD5((bid*random()*bbalance+?)::char))>substr(?,((random()*(?-?)+?)::integer),?) order by (bbalance*random()*bid+?) desc;
calls       | 53855
total_time  | 50795183.089
rows        | 184638251
hit_percent | 99.9981448776361289
-[ RECORD 2 ]--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
query       | UPDATE pgbench_accounts SET abalance = abalance + ? WHERE aid = ?;
calls       | 100
total_time  | 37131467.568
rows        | 100
hit_percent | 99.0085070587482269
-[ RECORD 3 ]--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
query       | SELECT abalance FROM pgbench_accounts WHERE aid = ?;
calls       | 100
total_time  | 27716213.981
rows        | 100
hit_percent | 71.5744839710469907
-[ RECORD 4 ]--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
query       | UPDATE pgbench_branches SET bbalance = bbalance + ? WHERE bid = ?;
calls       | 4164050
total_time  | 25529446.8299978
rows        | 4164050
hit_percent | 99.9986231277663809
-[ RECORD 5 ]--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
query       | UPDATE pgbench_tellers SET tbalance = tbalance + ? WHERE tid = ?;
calls       | 4164070
total_time  | 5094847.07700154
rows        | 4164070
hit_percent | 99.9976317644971741

Répertorier des requêtes sur base par exécution pour essayer des requêtes au fil du temps :

SELECT query, calls, total_time/calls as avg_time_ms, rows/calls as avg_rows,
temp_blks_read/calls as avg_tmp_read, temp_blks_written/calls as avg_temp_written
FROM pg_stat_statements
WHERE calls != 0
ORDER BY total_time DESC LIMIT 5;

Exemple de sortie :

benchmark=> SELECT query, calls, total_time/calls as avg_time_ms, rows/calls as avg_rows,
temp_blks_read/calls as avg_tmp_read, temp_blks_written/calls as avg_temp_written
FROM pg_stat_statements
WHERE calls != 0
ORDER BY total_time DESC LIMIT 5;
-[ RECORD 1 ]----+--------------------------------------------------------------
query            | SELECT MD5((bbalance*random()*bid+?)::char) from pgbench_branches where (MD5((bid*random()*bbalance+?)::char))>substr(?,((random()*(?-?)+?)::integer),?) order by (bbalance*random()*bid+?) desc;
calls            | 53855
avg_time_ms      | 943.184162826107
avg_rows         | 3428
avg_tmp_read     | 0
avg_temp_written | 0
-[ RECORD 2 ]----+--------------------------------------------------------------
query            | UPDATE pgbench_accounts SET abalance = abalance + ? WHERE aid = ?;
calls            | 100
avg_time_ms      | 371314.67568
avg_rows         | 1
avg_tmp_read     | 0
avg_temp_written | 0
-[ RECORD 3 ]----+--------------------------------------------------------------
query            | SELECT abalance FROM pgbench_accounts WHERE aid = ?;
calls            | 100
avg_time_ms      | 277162.13981
avg_rows         | 1
avg_tmp_read     | 0
avg_temp_written | 0
-[ RECORD 4 ]----+--------------------------------------------------------------
query            | UPDATE pgbench_branches SET bbalance = bbalance + ? WHERE bid = ?;
calls            | 4164050
avg_time_ms      | 6.13091745536144
avg_rows         | 1
avg_tmp_read     | 0
avg_temp_written | 0
-[ RECORD 5 ]----+--------------------------------------------------------------
query            | UPDATE pgbench_tellers SET tbalance = tbalance + ? WHERE tid = ?;
calls            | 4164070
avg_time_ms      | 1.22352579975878
avg_rows         | 1
avg_tmp_read     | 0
avg_temp_written | 0

Après avoir vérifié les exemples de sorties de l'exécution précédente, la requête suivante est identifié comme la source de l’utilisation intensive du CPU :

SELECT MD5((bbalance*random()*bid+?)::char) from pgbench_branches where (MD5((bid*random()*bbalance+?)::char))>substr(?,((random()*(?-?)+?)::integer),?) order by (bbalance*random()*bid+?) desc;

Dans la sortie, cette requête avait la durée d'exécution totale la plus élevée. Cette requête a été exécutée 53 855 fois et chaque exécution prenait en moyenne 943 millisecondes.

Paramètres de journalisation PostgreSQL

Activer la journalisation des requêtes à l'aide d'Amazon RDS for PostgreSQL. Ensuite, vérifiez les journaux des erreurs PostgreSQL afin de confirmer que vos paramètres log_min_duration_statement et log_statement sont définis sur les valeurs appropriées. Pour plus d'informations, consultez la documentation PostgreSQL pour Signalement et journalisation des erreurs.


Cette page vous a-t-elle été utile ?

Cette page peut-elle être améliorée ?


Vous avez besoin d'aide ?