Aller au contenu
medium

Superviser et maintenir MySQL : Performance Schema, requêtes lentes et OPTIMIZE

33 min de lecture

Un MySQL en production qui ralentit progressivement : requêtes passant de 2 ms à 200 ms, connexions qui s’accumulent, tables InnoDB qui grossissent sans raison apparente — c’est le scénario classique quand personne ne surveille. MySQL embarque un système d’instrumentation completPerformance Schema, sys schema et slow query log — qui permet de diagnostiquer chaque problème, mais encore faut-il savoir quoi regarder et quand agir.

Ce guide suit une démarche observer → identifier → expliquer → corriger. Chaque commande est testée sur un lab réel (MySQL 8.4 LTS, Debian 12, base lab_mysql avec des tables clients, commandes et logs). Vous saurez lire les vues Performance Schema, interpréter un plan d’exécution, détecter les tables fragmentées et utiliser OPTIMIZE TABLE, ANALYZE TABLE et CHECK TABLE au bon moment.

  • Diagnostiquer l’activité en cours avec SHOW PROCESSLIST et performance_schema.processlist
  • Identifier les requêtes lentes avec le slow query log et sys.statement_analysis
  • Lire un plan EXPLAIN ANALYZE et comprendre ALL, ref, range, Index Scan
  • Surveiller InnoDB : buffer pool hit ratio, redo log, transactions et purge lag
  • Maintenir les performances avec OPTIMIZE TABLE, ANALYZE TABLE et CHECK TABLE
  • Intégrer MySQL dans un monitoring Prometheus/Grafana

Vous avez besoin de superviser et maintenir MySQL dans ces situations :

  • Votre application ralentit progressivement et vous soupçonnez la base de données
  • Vous voulez mettre en place une supervision proactive avant que les problèmes n’apparaissent
  • Des requêtes lentes apparaissent dans les logs applicatifs mais vous ne savez pas lesquelles côté base
  • Vous devez identifier les requêtes les plus coûteuses pour savoir où optimiser (index manquants, full table scans)
  • Vous préparez un plan de maintenance pour une base en production (fragmentation, statistiques obsolètes)
  • Vous debuggez un problème de verrous ou de connexions qui s’accumulent
  • MySQL 8.4 installé et le service actif — voir le guide d’installation
  • Accès au compte root MySQL
  • Connaître les bases du client mysql — voir le guide de prise en main
  • Une base avec des données pour observer quelque chose (le lab utilise lab_mysql avec les tables clients, commandes, logs)

La démarche : observer → identifier → expliquer → corriger

Section intitulée « La démarche : observer → identifier → expliquer → corriger »

Diagnostiquer un problème de performance MySQL suit toujours le même ordre :

  1. Observer — que se passe-t-il en ce moment ? (SHOW PROCESSLIST, connexions, verrous)
  2. Identifier — quelles requêtes consomment le plus ? (slow query log, sys.statement_analysis)
  3. Expliquer — pourquoi cette requête est-elle lente ? (EXPLAIN ANALYZE, plan d’exécution)
  4. Corriger — ajouter un index, réécrire la requête, ou maintenir (OPTIMIZE TABLE, ANALYZE TABLE)

Ne sautez pas d’étape. Un OPTIMIZE TABLE lancé à l’aveugle sur une table de production verrouille tout en écriture avec l’algorithme par défaut. Un index ajouté sans avoir lu le plan peut aggraver la situation.

SHOW PROCESSLIST et performance_schema.processlist

Section intitulée « SHOW PROCESSLIST et performance_schema.processlist »

SHOW PROCESSLIST est le premier réflexe pour voir toutes les connexions actives :

SHOW PROCESSLIST;
+----+-----------------+-----------+-----------+---------+------+------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------+-----------+---------+------+------------------------+------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 842 | Waiting on empty queue | NULL |
| 8 | root | localhost | lab_mysql | Query | 0 | init | SHOW PROCESSLIST |
+----+-----------------+-----------+-----------+---------+------+------------------------+------------------+

Limitation : SHOW PROCESSLIST tronque la colonne Info à 100 caractères. Utilisez SHOW FULL PROCESSLIST ou la vue Performance Schema pour les requêtes complètes :

SELECT ID,
USER,
HOST,
DB,
COMMAND,
TIME AS time_sec,
STATE,
LEFT(INFO, 80) AS query
FROM performance_schema.processlist
WHERE COMMAND <> 'Daemon';
ColonneSignification
IdIdentifiant de la connexion (utilisable avec KILL)
CommandType d’opération (Query, Sleep, Connect, Daemon)
TimeSecondes depuis le début de l’état courant
StateÉtape interne (sending data, executing, Waiting for lock…)
InfoRequête en cours d’exécution
SELECT COMMAND, COUNT(*) AS cnt
FROM performance_schema.processlist
GROUP BY COMMAND;
CommandSignificationAction si trop nombreux
QueryExécute une requêteNormal, vérifier si trop long
SleepConnecté, ne fait rienPool de connexions mal configuré ou wait_timeout trop élevé
DaemonProcessus interne (event_scheduler)Normal
-- Annuler la requête en cours (connexion reste ouverte)
KILL QUERY 42;
-- Tuer la connexion entière
KILL CONNECTION 42;
-- Synonyme court
KILL 42;

SHOW GLOBAL STATUS expose des centaines de compteurs cumulatifs. Voici les plus utiles :

SHOW GLOBAL STATUS WHERE Variable_name IN (
'Threads_connected', 'Threads_running',
'Max_used_connections', 'Connections',
'Slow_queries', 'Questions',
'Innodb_buffer_pool_read_requests', 'Innodb_buffer_pool_reads',
'Innodb_row_lock_waits', 'Innodb_row_lock_time'
);
CompteurSignification
Threads_connectedConnexions actuellement ouvertes
Threads_runningConnexions exécutant une requête en ce moment
Max_used_connectionsPic de connexions simultanées depuis le démarrage
Slow_queriesNombre total de requêtes lentes détectées
Innodb_buffer_pool_read_requestsLectures logiques (depuis le buffer pool)
Innodb_buffer_pool_readsLectures physiques (depuis le disque)
Innodb_row_lock_waitsAttentes de verrou de ligne
Innodb_row_lock_timeTemps total passé en attente de verrous (ms)

Le buffer pool hit ratio se calcule ainsi :

SELECT
(1 - (
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') /
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests')
)) * 100 AS buffer_pool_hit_ratio;

Un ratio supérieur à 99 % est normal pour une charge OLTP. En dessous de 95 %, augmentez innodb_buffer_pool_size — voir le guide de configuration.

SHOW ENGINE INNODB STATUS : le diagnostic instantané

Section intitulée « SHOW ENGINE INNODB STATUS : le diagnostic instantané »

SHOW ENGINE INNODB STATUS\G affiche un rapport complet sur l’état interne d’InnoDB : transactions actives, verrous, buffer pool, I/O, redo log. C’est l’outil de diagnostic le plus riche de MySQL :

SHOW ENGINE INNODB STATUS\G

Les sections clés du rapport :

SectionCe qu’elle montre
SEMAPHORESAttentes de verrous internes (mutex, rw-lock)
TRANSACTIONSTransactions actives, historique, purge
FILE I/OThreads I/O, opérations en attente
BUFFER POOL AND MEMORYTaille du pool, pages free/dirty/data, hit rate
ROW OPERATIONSINSERT/UPDATE/DELETE par seconde
LOGPosition du redo log, séquence LSN
DEADLOCKSDernier deadlock détecté par InnoDB

Le slow query log enregistre toutes les requêtes dont le temps d’exécution dépasse un seuil. C’est l’équivalent MySQL de la supervision par pg_stat_statements en PostgreSQL — mais sous forme de fichier de log.

-- Activer sans redémarrer (SET PERSIST pour survivre au reboot)
SET PERSIST slow_query_log = ON;
SET PERSIST slow_query_log_file = '/var/lib/mysql/slow.log';
SET PERSIST long_query_time = 1; -- seuil en secondes

Vérification :

SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';
+---------------------+---------------------------+
| Variable_name | Value |
+---------------------+---------------------------+
| slow_query_log | ON |
| slow_query_log_file | /var/lib/mysql/slow.log |
+---------------------+---------------------------+
+-------------------+----------+
| Variable_name | Value |
+-------------------+----------+
| long_query_time | 1.000000 |
+-------------------+----------+
ValeurUsage
10 (défaut)Trop élevé — ne capture que les requêtes catastrophiques
1Bon point de départ pour la production
0.5Pour un audit approfondi
0Capture toutes les requêtes — attention à l’espace disque et aux I/O

Active la capture des requêtes qui ne utilisent aucun index, même si elles sont rapides :

SET PERSIST log_queries_not_using_indexes = ON;
-- Limiter le nombre de logs par minute pour éviter l'explosion
SET PERSIST log_throttle_queries_not_using_indexes = 10;

C’est un excellent moyen de détecter les full table scans sur des tables qui grossiront : une requête rapide aujourd’hui sur 1 000 lignes deviendra lente sur 1 million.

mysqldumpslow agrège les entrées du slow log et les classe par fréquence, temps moyen ou total :

Fenêtre de terminal
# Top 10 des requêtes par temps total
mysqldumpslow -s t -t 10 /var/lib/mysql/slow.log
# Top 10 par nombre d'appels
mysqldumpslow -s c -t 10 /var/lib/mysql/slow.log
# Top 10 par temps moyen
mysqldumpslow -s at -t 10 /var/lib/mysql/slow.log
Count: 12 Time=2.34s (28s) Lock=0.00s (0s) Rows=90000.0 (1080000)
SELECT * FROM logs WHERE action = 'S'

Lecture : cette requête a été exécutée 12 fois, avec un temps moyen de 2,34 s et un temps total de 28 s. C’est la priorité d’optimisation.

Performance Schema est le système d’instrumentation interne de MySQL. Il collecte des statistiques sur tout : requêtes, verrous, I/O, mémoire, threads, étapes internes. Activé par défaut depuis MySQL 5.6, sa consommation mémoire dépend du nombre d’instruments et de consumers activés — vérifiez avec SHOW ENGINE PERFORMANCE_SCHEMA STATUS.

SHOW VARIABLES LIKE 'performance_schema';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| performance_schema | ON |
+--------------------+-------+

Performance Schema fonctionne en deux couches :

  • Instruments : les points de mesure (requêtes, verrous, I/O, mémoire…)
  • Consumers : les tables qui stockent les données collectées
-- Nombre d'instruments disponibles
SELECT COUNT(*) FROM performance_schema.setup_instruments;
-- ~1200 instruments
-- Instruments pour les statements (requêtes)
SELECT NAME, ENABLED, TIMED
FROM performance_schema.setup_instruments
WHERE NAME LIKE 'statement/sql/%'
LIMIT 5;
+----------------------------+---------+-------+
| NAME | ENABLED | TIMED |
+----------------------------+---------+-------+
| statement/sql/select | YES | YES |
| statement/sql/insert | YES | YES |
| statement/sql/update | YES | YES |
| statement/sql/delete | YES | YES |
| statement/sql/create_table | YES | YES |
+----------------------------+---------+-------+

Performance Schema collecte les requêtes dans plusieurs tables :

TableContenu
events_statements_currentRequêtes en cours d’exécution
events_statements_historyDernières requêtes par thread
events_statements_history_longHistorique étendu (configurable)
events_statements_summary_by_digestStatistiques agrégées par type de requête (normalisation)
events_statements_summary_by_user_by_event_namePar utilisateur

La vue sys.statement_analysis est l’équivalent MySQL du top 10 pg_stat_statements de PostgreSQL — prête à l’emploi, sans installation :

SELECT query,
exec_count,
avg_latency,
total_latency,
rows_sent_avg,
rows_examined_avg,
full_scan
FROM sys.statement_analysis
LIMIT 10;
+--------------------------------------+------------+-------------+---------------+---------------+-------------------+-----------+
| query | exec_count | avg_latency | total_latency | rows_sent_avg | rows_examined_avg | full_scan |
+--------------------------------------+------------+-------------+---------------+---------------+-------------------+-----------+
| SELECT * FROM `logs` WHERE `action` | 12 | 2.34 s | 28.08 s | 7500 | 90000 | * |
| SELECT `nom` , `email` FROM `cl ... | 45 | 120.50 ms | 5.42 s | 6 | 6 | |
| INSERT INTO `commandes` ( `clie ... | 200 | 1.20 ms | 240.00 ms | 0 | 0 | |
+--------------------------------------+------------+-------------+---------------+---------------+-------------------+-----------+

Ce qu’il faut regarder en premier :

  • total_latency élevé : la requête qui consomme le plus de temps serveur au total — priorité n°1
  • avg_latency élevé + exec_count bas : requête unitairement lente (index manquant ?)
  • full_scan = * : la requête fait un full table scan — index manquant
  • rows_examined_avg >> rows_sent_avg : MySQL lit beaucoup trop de lignes pour en retourner peu

Pour voir quelles tables occupent le buffer pool :

SELECT object_schema, object_name,
allocated, data, pages
FROM sys.innodb_buffer_stats_by_table
WHERE object_schema NOT IN ('mysql', 'sys', 'performance_schema')
LIMIT 5;

Le sys schema est une collection de vues, fonctions et procédures qui simplifient l’exploitation de Performance Schema. Installé par défaut depuis MySQL 5.7.

SELECT thd_id, conn_id, user, db,
command, state, time,
LEFT(current_statement, 60) AS query,
trx_latency, lock_latency
FROM sys.processlist
WHERE conn_id IS NOT NULL;

L’avantage par rapport à SHOW PROCESSLIST : la vue ajoute trx_latency (durée de la transaction) et lock_latency (temps passé en attente de verrou).

Vue consolidée de l’activité par table :

SELECT table_schema, table_name,
rows_fetched, rows_inserted, rows_updated, rows_deleted,
io_read, io_write
FROM sys.schema_table_statistics
WHERE table_schema = 'lab_mysql';

Détecte les index redondants (un index qui est un préfixe d’un autre) :

SELECT table_schema, table_name,
redundant_index_name, redundant_index_columns,
dominant_index_name, dominant_index_columns
FROM sys.schema_redundant_indexes
WHERE table_schema = 'lab_mysql';

Un index redondant consomme de l’espace et ralentit les écritures (INSERT, UPDATE, DELETE) sans apporter de bénéfice aux lectures. Supprimez-le après vérification.

Pour identifier les clients ou utilisateurs les plus actifs :

-- Par hôte
SELECT host, statements, total_latency, rows_examined, full_scans
FROM sys.host_summary;
-- Par utilisateur
SELECT user, statements, total_latency, rows_examined, full_scans
FROM sys.user_summary;

EXPLAIN montre le plan que MySQL prévoit d’utiliser pour exécuter une requête, sans l’exécuter :

EXPLAIN SELECT * FROM logs WHERE action = 'login' AND client_id = 3;
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+
| 1 | SIMPLE | logs | ALL | NULL | NULL | NULL | NULL | 90000 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+-------+-------------+

type = ALL signifie un full table scan : MySQL lit les 90 000 lignes pour en filtrer quelques-unes. C’est le pire cas possible.

EXPLAIN ANALYZE exécute réellement la requête et mesure le temps de chaque étape :

EXPLAIN ANALYZE SELECT * FROM logs WHERE action = 'login' AND client_id = 3\G
-> Filter: ((logs.action = 'login') AND (logs.client_id = 3)) (cost=9044 rows=900)
(actual time=0.046..12.532 rows=4985 loops=1)
-> Table scan on logs (cost=9044 rows=90000)
(actual time=0.039..7.284 rows=90000 loops=1)

Lecture : MySQL scanne les 90 000 lignes (Table scan) en 7,3 ms, puis filtre pour ne garder que 4 985 lignes en 12,5 ms au total. L’estimateur prévoyait 900 lignes — 5× moins que la réalité. Les statistiques sont obsolètes.

-- Format arbre (plus lisible, recommandé depuis 8.0)
EXPLAIN FORMAT=TREE SELECT * FROM logs WHERE action = 'login'\G
-- Format JSON (détail complet, utile pour les outils)
EXPLAIN FORMAT=JSON SELECT * FROM logs WHERE action = 'login'\G

MySQL 8.4 introduit EXPLAIN INTO pour stocker le plan dans une variable utilisable ensuite. EXPLAIN INTO exige FORMAT=JSON et n’est pas compatible avec EXPLAIN ANALYZE :

EXPLAIN FORMAT=JSON INTO @plan
SELECT * FROM logs WHERE action = 'login';
SELECT JSON_PRETTY(@plan)\G
CREATE INDEX idx_logs_action_client ON logs (action, client_id);
EXPLAIN ANALYZE SELECT * FROM logs WHERE action = 'login' AND client_id = 3\G
-> Index lookup on logs using idx_logs_action_client
(action='login', client_id=3) (cost=1743 rows=4985)
(actual time=0.052..2.463 rows=4985 loops=1)

De 12,5 ms (full table scan) à 2,5 ms (index lookup) — 5× plus rapide.

La colonne type de EXPLAIN indique le mode d’accès, du pire au meilleur :

TypeSignificationPerformance
ALLFull table scan — lit toutes les lignesPire
indexFull index scan — parcourt tout l’indexMauvais
rangeScan partiel d’index (BETWEEN, <, >, IN)Acceptable
refLookup par index non-uniqueBon
eq_refLookup par index unique (jointure PK)Très bon
constLookup par clé primaire, résultat uniqueOptimal
systemTable à une seule ligneOptimal

Règle : si vous voyez ALL sur une table de plus de 10 000 lignes avec un WHERE, c’est un index manquant.

Le buffer pool est le cache mémoire d’InnoDB. C’est la métrique la plus importante pour les performances :

SHOW ENGINE INNODB STATUS\G

Extrait de la section BUFFER POOL AND MEMORY :

Buffer pool size 16384
Free buffers 15130
Database pages 1247
Old database pages 440
Modified db pages 0
Buffer pool hit rate 1000 / 1000
MétriqueSignification
Buffer pool sizePages totales dans le pool (× 16 Ko)
Free buffersPages libres disponibles
Database pagesPages contenant des données
Modified db pagesPages dirty (modifiées, pas encore écrites sur disque)
Buffer pool hit rate1000/1000 = 100 % — parfait. En dessous de 950/1000 (95 %), augmentez le buffer pool

Pour un monitoring automatisé :

SELECT
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests') AS logical_reads,
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') AS disk_reads,
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_pages_dirty') AS dirty_pages,
(SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_pages_total') AS total_pages;

Le redo log (journal de transactions InnoDB) garantit la durabilité des données. Si le redo log se remplit plus vite que les checkpoints ne le libèrent, MySQL ralentit :

SHOW ENGINE INNODB STATUS\G

Section LOG :

Log sequence number 79456832
Log buffer assigned up to 79456832
Log buffer completed up to 79456832
Log written up to 79456832
Log flushed up to 79456832
Last checkpoint at 79456832

Si Log sequence number est très en avance sur Last checkpoint at, le redo log est sous pression — les écritures pourraient être ralenties.

-- Vérifier la taille du redo log
SHOW VARIABLES LIKE 'innodb_redo_log_capacity';
+--------------------------+-----------+
| Variable_name | Value |
+--------------------------+-----------+
| innodb_redo_log_capacity | 104857600 |
+--------------------------+-----------+

100 Mo par défaut en MySQL 8.4. Pour une base avec beaucoup d’écritures, augmentez à 256 Mo ou 512 Mo :

SET PERSIST innodb_redo_log_capacity = 268435456; -- 256 Mo

InnoDB maintient un historique de transactions pour le MVCC (lectures cohérentes sans verrou). Si l’undo log grossit, la purge est en retard :

SHOW ENGINE INNODB STATUS\G

Section TRANSACTIONS :

Trx id counter 12345
Purge done for trx's n:o < 12340 undo n:o < 0 state: running
History list length 23
MétriqueSignificationSeuil d’alerte
History list lengthTransactions en attente de purge> 10 000

Un History list length qui augmente indique que le thread de purge ne suit pas le rythme des écritures. Causes possibles : requêtes SELECT très longues qui maintiennent une vue MVCC ancienne, ou innodb_purge_threads insuffisant.

L’adaptive hash index (AHI) est un mécanisme automatique d’InnoDB qui crée un index hash en mémoire pour accélérer les lookups fréquents :

SHOW VARIABLES LIKE 'innodb_adaptive_hash_index';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| innodb_adaptive_hash_index | OFF |
+----------------------------+-------+

En MySQL 8.4, l’AHI est désactivé par défaut (il était activé en 8.0). Si vous l’avez activé manuellement et voulez vérifier s’il est utile, consultez SHOW ENGINE INNODB STATUS section INSERT BUFFER AND ADAPTIVE HASH INDEX :

  • hash searches/s élevé et non-hash searches/s faible : l’AHI est efficace, gardez-le
  • hash searches/s faible ou 0 : l’AHI consomme de la mémoire pour rien — désactivez-le
-- Activer pour tester (dynamique, pas de redémarrage)
SET PERSIST innodb_adaptive_hash_index = ON;
-- Désactiver si le monitoring ne montre pas de bénéfice
SET PERSIST innodb_adaptive_hash_index = OFF;

L’optimiseur de requêtes MySQL utilise des statistiques sur la distribution des données pour choisir le meilleur plan. Si ces statistiques sont obsolètes (après un gros chargement ou des suppressions massives), l’optimiseur fait de mauvais choix.

ANALYZE TABLE lab_mysql.logs;
+-----------------+---------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+-----------------+---------+----------+----------+
| lab_mysql.logs | analyze | status | OK |
+-----------------+---------+----------+----------+

ANALYZE TABLE est généralement rapide sur InnoDB, mais il prend un read lock pendant l’analyse. Planifiez-le de préférence hors pic si la table est très sollicitée en écriture. Exécutez-le après tout chargement massif.

Les histogrammes donnent à l’optimiseur une vision détaillée de la distribution des valeurs dans une colonne — pas seulement la cardinalité. Particulièrement utile pour les colonnes à distribution non uniforme :

ANALYZE TABLE lab_mysql.logs UPDATE HISTOGRAM ON action, client_id;
+-----------------+---------+----------+----------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+-----------------+---------+----------+----------------------------------------------+
| lab_mysql.logs | histogram | status | Histogram statistics created for column 'action'. |
| lab_mysql.logs | histogram | status | Histogram statistics created for column 'client_id'. |
+-----------------+---------+----------+----------------------------------------------+

Vérifier les histogrammes créés :

SELECT SCHEMA_NAME, TABLE_NAME, COLUMN_NAME,
JSON_EXTRACT(HISTOGRAM, '$.\"histogram-type\"') AS type,
JSON_EXTRACT(HISTOGRAM, '$.\"number-of-buckets-specified\"') AS buckets
FROM information_schema.COLUMN_STATISTICS
WHERE SCHEMA_NAME = 'lab_mysql';

Après de nombreux DELETE ou UPDATE, les tables InnoDB conservent l’espace libre dans leurs pages. OPTIMIZE TABLE reconstruit la table et les index pour récupérer l’espace :

OPTIMIZE TABLE lab_mysql.logs;
+-----------------+----------+----------+-------------------------------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+-----------------+----------+----------+-------------------------------------------------------------------+
| lab_mysql.logs | optimize | note | Table does not support optimize, doing recreate + analyze instead |
| lab_mysql.logs | optimize | status | OK |
+-----------------+----------+----------+-------------------------------------------------------------------+

CHECK TABLE vérifie l’intégrité d’une table :

CHECK TABLE lab_mysql.clients, lab_mysql.commandes, lab_mysql.logs;
+----------------------+-------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+----------------------+-------+----------+----------+
| lab_mysql.clients | check | status | OK |
| lab_mysql.commandes | check | status | OK |
| lab_mysql.logs | check | status | OK |
+----------------------+-------+----------+----------+

En ligne de commande, mysqlcheck permet de vérifier, optimiser et analyser en masse :

Fenêtre de terminal
# Vérifier toutes les tables d'une base
mysqlcheck -u root -p lab_mysql
# Optimiser toutes les tables
mysqlcheck -u root -p --optimize lab_mysql
# Analyser toutes les tables de toutes les bases
mysqlcheck -u root -p --all-databases --analyze

Pour une supervision continue, installez mysqld_exporter (Prometheus) :

Fenêtre de terminal
# Créer un utilisateur dédié pour l'exporter
mysql -u root -p -e "
CREATE USER 'exporter'@'localhost'
IDENTIFIED BY 'motdepasse_exporter'
WITH MAX_USER_CONNECTIONS 3;
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'localhost';
"

Le fichier de configuration .my.cnf pour l’exporter :

[client]
user=exporter
password=motdepasse_exporter

Métriques essentielles exposées par mysqld_exporter :

Métrique PrometheusSource MySQLSeuil d’alerte
mysql_global_status_threads_connectedThreads_connected> 80 % de max_connections
mysql_global_status_slow_queriesSlow_queriesAugmentation soudaine
mysql_global_status_innodb_buffer_pool_readsInnodb_buffer_pool_readsHit ratio < 95 %
mysql_global_status_innodb_row_lock_waitsInnodb_row_lock_waits> 0 de manière soutenue
mysql_slave_status_seconds_behind_masterSeconds_Behind_Source> 30 secondes

Le dashboard Grafana le plus populaire pour MySQL : MySQL Overview (ID 7362 sur grafana.com). Il affiche le buffer pool hit ratio, les connexions, les QPS, le lag de réplication et les métriques InnoDB.

AlerteConditionCriticité
Connexions proches de la limiteThreads_connected > 80 % max_connectionsCritique
Buffer pool hit ratio faible< 95 % pendant 5 minWarning
Lag de réplicationSeconds_Behind_Source > 60 sCritique
Réplication casséeReplica_IO_Running = No ou Replica_SQL_Running = NoCritique
Slow queries en hausse+50 % par rapport à la baselineWarning
Espace disque< 20 % libre sur le datadirCritique
Deadlocks fréquents> 10 par heureWarning
SymptômeCause probableSolution
Requête bloquée par un lockTransaction longue non commitéeIdentifier avec SELECT * FROM sys.innodb_lock_waits\G, tuer la transaction bloquante
Buffer pool hit ratio < 95 %innodb_buffer_pool_size trop petitAugmenter à 50-70 % de la RAM disponible
Slow queries en augmentation soudaineIndex supprimé ou statistiques obsolètesANALYZE TABLE, vérifier les index avec sys.schema_redundant_indexes
”Too many connections”Pool non fermé ou wait_timeout trop élevéRéduire wait_timeout, utiliser un connection pooler (ProxySQL)
Table qui grossit après DELETE/UPDATEEspace non récupéré au niveau fichierOPTIMIZE TABLE (relâche l’espace)
EXPLAIN montre ALL sur une grosse tableIndex manquant sur les colonnes filtréesAjouter un index composite sur les colonnes du WHERE
History list length qui augmenteThread de purge en retard, SELECT très longsIdentifier les transactions longues, augmenter innodb_purge_threads
Deadlocks fréquentsOrdre d’accès aux tables/lignes incohérentToujours accéder dans le même ordre, transactions courtes, innodb_print_all_deadlocks = ON
  • SHOW PROCESSLIST (ou sys.processlist) est le premier réflexe : qui est connecté, que fait-il, depuis combien de temps.
  • sys.statement_analysis identifie les requêtes coûteuses : total_latency, full_scan, rows_examined vs rows_sent — l’équivalent MySQL de pg_stat_statements.
  • EXPLAIN ANALYZE est le seul moyen fiable de comprendre pourquoi une requête est lente — regardez type = ALL (full scan) et rows examined >> rows sent.
  • Le slow query log capture les requêtes lentes sur disque — activez log_queries_not_using_indexes pour détecter les full scans avant qu’ils ne posent problème.
  • Le buffer pool hit ratio (>99 % en OLTP) est la métrique InnoDB la plus critique — si elle chute, augmentez innodb_buffer_pool_size.
  • ANALYZE TABLE met à jour les statistiques de l’optimiseur — exécutez-le après tout chargement massif. Les histogrammes améliorent l’estimation pour les distributions non uniformes.
  • OPTIMIZE TABLE reconstruit la table InnoDB et récupère l’espace — nécessite de l’espace disque temporaire, préférez les heures creuses.
  • CHECK TABLE vérifie l’intégrité sans réparer — premier diagnostic en cas de corruption suspectée.
  • En production, installez mysqld_exporter + Grafana pour une supervision continue : buffer pool, connexions, lag de réplication, slow queries.
  • L’alerte la plus importante : réplication cassée (Replica_IO_Running = No) — toute minute sans réplication est une minute de données non protégées.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn