| name | mysql-tuner |
| description | Optimisation MySQL/MariaDB incluant slow query log, stratégie d'index, tuning InnoDB, réplication et monitoring. Se déclenche avec "MySQL", "MariaDB", "slow query", "InnoDB", "MySQL tuning", "requête MySQL lente. Also triggers on "MySQL slow query", "InnoDB tuning", "MariaDB performance". |
MySQL Tuner
Workflow
1. Activer et analyser le slow query log
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_slow_extra = 1
log_queries_not_using_indexes = 1
Analyser avec pt-query-digest (Percona Toolkit) :
pt-query-digest /var/log/mysql/slow.log \
--limit 20 \
--output report \
> /tmp/digest.txt
Critères de triage : rank par rows_examined / rows_sent (ratio > 100 = suspect), puis par durée totale cumulée.
2. Auditer les index existants
SELECT * FROM sys.statements_with_full_table_scans
ORDER BY no_index_used_count DESC LIMIT 20;
SELECT * FROM sys.schema_unused_indexes
WHERE object_schema NOT IN ('mysql','information_schema','performance_schema');
SELECT * FROM sys.schema_redundant_indexes;
EXPLAIN ANALYZE sur toute requête suspecte (MySQL 8.0.18+) :
EXPLAIN ANALYZE
SELECT u.id, o.total
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE u.status = 'active' AND o.created_at > '2026-01-01';
Signaux d'alerte dans le plan : Full table scan, Using filesort, Using temporary, rows_examined >> rows_returned.
3. Concevoir la stratégie d'indexation
Règle du leftmost prefix — l'index (a, b, c) couvre WHERE a=?, WHERE a=? AND b=?, mais pas WHERE b=?.
Critères de décision :
| Cas | Solution |
|---|
| Filtre sur colonne haute cardinalité | Index simple |
| Filtre multi-colonnes fréquent | Index composite (sélectivité décroissante en tête) |
| SELECT ne lit que quelques colonnes | Covering index (col_filtre, col1_select, col2_select) |
| LIKE 'prefix%' | Index B-Tree classique (OK) |
| LIKE '%suffix' | Full-text index ou Elasticsearch |
| JSON path fréquent | Generated column + index |
ALTER TABLE orders
ADD INDEX idx_orders_user_date_total (user_id, created_at, total);
ALTER TABLE events
ADD COLUMN event_type VARCHAR(50) GENERATED ALWAYS AS (data->>'$.type') STORED,
ADD INDEX idx_event_type (event_type);
Avant tout ajout, mesurer l'impact write :
sysbench oltp_write_only --db-driver=mysql --mysql-db=bench \
--mysql-user=root --tables=10 --table-size=100000 prepare
sysbench oltp_write_only ... run
4. Tuner les paramètres InnoDB
innodb_buffer_pool_size = 12G
innodb_buffer_pool_instances = 8
innodb_log_file_size = 1G
innodb_flush_log_at_trx_commit = 1
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
innodb_read_io_threads = 8
innodb_write_io_threads = 8
innodb_flush_method = O_DIRECT
Vérifier le buffer pool hit ratio (cible > 99 %) :
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 hit_ratio_pct;
5. Optimiser la configuration serveur
max_connections = 300
thread_cache_size = 50
table_open_cache = 4000
table_definition_cache = 2000
tmp_table_size = 64M
max_heap_table_size = 64M
join_buffer_size = 4M
sort_buffer_size = 4M
Estimer la mémoire totale avant d'ajuster max_connections :
mémoire_max = innodb_buffer_pool_size
+ (max_connections × (sort_buffer_size + join_buffer_size + read_buffer_size + …))
6. Configurer la réplication (GTID)
server_id = 1
log_bin = /var/log/mysql/binlog
gtid_mode = ON
enforce_gtid_consistency = ON
binlog_format = ROW
sync_binlog = 1
server_id = 2
relay_log = /var/log/mysql/relaylog
read_only = ON
replica_preserve_commit_order = ON
Démarrer la réplication :
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='192.168.1.10',
SOURCE_USER='repl',
SOURCE_PASSWORD='xxx',
SOURCE_AUTO_POSITION=1;
START REPLICA;
SHOW REPLICA STATUS\G
Distribuer les lectures avec ProxySQL :
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup)
VALUES (10, 1, '^SELECT', 20);
LOAD MYSQL QUERY RULES TO RUNTIME;
7. Monitoring
Métriques clés à surveiller (Prometheus + mysqld_exporter ou PMM) :
| Métrique | Seuil alerte |
|---|
mysql_global_status_threads_running | > 30 |
| Buffer pool hit ratio | < 99 % |
mysql_global_status_slow_queries (rate) | hausse soudaine |
Replication lag (Seconds_Behind_Source) | > 30 s |
mysql_global_status_aborted_connects | > 0 croissant |
docker run -d -p 9104:9104 \
-e DATA_SOURCE_NAME="exporter:xxx@tcp(localhost:3306)/" \
prom/mysqld-exporter
8. Maintenance planifiée
SELECT table_name, data_free/1024/1024 AS free_mb
FROM information_schema.tables
WHERE table_schema = 'mydb' AND data_free > 100*1024*1024
ORDER BY data_free DESC;
ANALYZE TABLE orders;
Archivage automatique des vieilles données : préférer le partitionnement par RANGE sur created_at + ALTER TABLE ... DROP PARTITION.
Garde-fous / Anti-patterns / Pièges
- Ne jamais toucher
innodb_log_file_size à chaud — nécessite un arrêt propre + suppression des anciens iblogfile* sous MySQL 5.x ; MySQL 8 gère ça dynamiquement.
query_cache désactivé en MySQL 8+ — toute tentative de l'activer lève une erreur ; utiliser ProxySQL query cache ou un cache applicatif (Redis).
max_connections × buffers_par_connexion peut dépasser la RAM et tuer le serveur par OOM. Toujours calculer avant d'augmenter.
- Index sur colonnes de faible cardinalité (ex.
status avec 3 valeurs) inutile sauf en composite. L'optimiseur préférera un full scan.
OPTIMIZE TABLE sur InnoDB = rebuild complet + lock en 5.x ; utiliser pt-online-schema-change ou gh-ost en production.
- Réplication sans
sync_binlog=1 : risque de binlog corrompu en crash, GTID devient incohérent.
- Ne jamais modifier plusieurs paramètres simultanément — isoler chaque changement pour mesurer son effet réel.
EXPLAIN sans ANALYZE affiche des estimations qui peuvent être très éloignées de la réalité (histogrammes périmés).
Bonnes pratiques 2026
- Utiliser MySQL 8.4 LTS ou MariaDB 11.x ; éviter les branches EOL.
- Activer les histogrammes de colonnes (
ANALYZE TABLE … UPDATE HISTOGRAM ON col) pour les colonnes non-indexées dans les filtres.
- Préférer
gh-ost à pt-osc pour les ALTER sans verrou sur MySQL 8+ (meilleure gestion des GTID).
- Déployer ProxySQL 2.x avec multiplexage de connexions pour réduire la pression sur
max_connections.
- Activer
performance_schema en production (overhead < 1 % depuis MySQL 5.7) : indispensable pour diagnostiquer les mutex, waits et top statements.