🗄️ Bases de données

database-mysql-tuner

Optimisation MySQL/MariaDB incluant slow query log, stratégie d'index, tuning InnoDB, réplication et monitoring.

⚡ Installation & lancement en 1 commande

Copiez-collez dans votre terminal : le skill s'installe dans ~/.claude/skills et Claude Code se lance directement dessus.

macOS / Linux
curl -fsSL https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.sh | sh -s -- database-mysql-tuner --launch
Windows (PowerShell)
iex "& { $(iwr -useb https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.ps1) } database-mysql-tuner -Launch"

🚀 Déjà installé ?

claude "/database-mysql-tuner"

Ou tapez /database-mysql-tuner dans une session Claude Code, ou décrivez simplement votre besoin — le skill se déclenche automatiquement via le skill-router.

🔑 Déclencheurs automatiques

Le skill s'active automatiquement quand votre demande contient :

MySQLMariaDBslow queryInnoDBMySQL tuning

📦 Installation manuelle

git clone https://github.com/khalilbenaz/claude-skills-collection.git cp -r claude-skills-collection/skills/database-mysql-tuner ~/.claude/skills/

Payload du plugin : skills/database-mysql-tuner · source éditable : database-skills/mysql-tuner

📖 Manuel

MySQL Tuner

Workflow

1. Activer et analyser le slow query log

# my.cnf / my.ini
slow_query_log        = 1
slow_query_log_file   = /var/log/mysql/slow.log
long_query_time       = 1        # secondes ; descendre à 0.5 en prod chargée
log_slow_extra        = 1        # MySQL 8.0.14+ : rows_examined, tmp_tables…
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

-- Requêtes sans index en production (MySQL 8+)
SELECT * FROM sys.statements_with_full_table_scans
ORDER BY no_index_used_count DESC LIMIT 20;

-- Index jamais utilisés
SELECT * FROM sys.schema_unused_indexes
WHERE object_schema NOT IN ('mysql','information_schema','performance_schema');

-- Doublons d'index
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 :

CasSolution
Filtre sur colonne haute cardinalitéIndex simple
Filtre multi-colonnes fréquentIndex composite (sélectivité décroissante en tête)
SELECT ne lit que quelques colonnesCovering index (col_filtre, col1_select, col2_select)
LIKE 'prefix%'Index B-Tree classique (OK)
LIKE '%suffix'Full-text index ou Elasticsearch
JSON path fréquentGenerated column + index
-- Covering index exemple
ALTER TABLE orders
  ADD INDEX idx_orders_user_date_total (user_id, created_at, total);

-- Index sur colonne générée (JSON)
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

# Règle de base : 70-80 % de la RAM dédiée MySQL
innodb_buffer_pool_size      = 12G          # ex. serveur 16 Go
innodb_buffer_pool_instances = 8            # 1 par Go, max 64
innodb_log_file_size         = 1G           # viser 1 heure de redo log
innodb_flush_log_at_trx_commit = 1          # 0/2 = perf + risque perte données
innodb_io_capacity           = 2000         # SSD NVMe ; 200 pour HDD
innodb_io_capacity_max       = 4000
innodb_read_io_threads       = 8
innodb_write_io_threads      = 8
innodb_flush_method          = O_DIRECT     # évite double buffering OS

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        # jamais > RAM / mémoire_par_connexion
thread_cache_size      = 50
table_open_cache       = 4000
table_definition_cache = 2000
tmp_table_size         = 64M        # tables temp en mémoire
max_heap_table_size    = 64M
join_buffer_size       = 4M         # par thread — ne pas exagérer
sort_buffer_size       = 4M         # idem
# MySQL 8+ : query_cache supprimé — ne pas tenter de l'activer

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)

# Source
server_id               = 1
log_bin                 = /var/log/mysql/binlog
gtid_mode               = ON
enforce_gtid_consistency = ON
binlog_format           = ROW
sync_binlog             = 1

# Replica
server_id               = 2
relay_log               = /var/log/mysql/relaylog
read_only               = ON
replica_preserve_commit_order = ON   # MySQL 8.0+

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;    -- GTID
START REPLICA;
SHOW REPLICA STATUS\G        -- vérifier Seconds_Behind_Source = 0

Distribuer les lectures avec ProxySQL :

-- ProxySQL admin
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup)
VALUES (10, 1, '^SELECT', 20);  -- hostgroup 20 = replicas
LOAD MYSQL QUERY RULES TO RUNTIME;

7. Monitoring

Métriques clés à surveiller (Prometheus + mysqld_exporter ou PMM) :

MétriqueSeuil 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
# mysqld_exporter (Prometheus)
docker run -d -p 9104:9104 \
  -e DATA_SOURCE_NAME="exporter:xxx@tcp(localhost:3306)/" \
  prom/mysqld-exporter

8. Maintenance planifiée

-- Fragmentation des tables (InnoDB — rarement nécessaire, préférer ALTER ... ENGINE=InnoDB)
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;

-- Reconstruire proprement sans lock (pt-online-schema-change)
-- pt-osc --alter "ENGINE=InnoDB" D=mydb,t=big_table --execute

-- Statistiques d'index
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

Bonnes pratiques 2026