TECHNOLOGIES / MySQL

MySQL : la base de données relationnelle de nos projets Symfony

MySQL est le système de gestion de base de données relationnelle open source le plus utilisé au monde. Chez SmartBooster, il equipe l'ensemble de nos projets Symfony via Doctrine ORM, conteneurisé dans notre stack Docker standard.

Cette page vous explique pourquoi nous avons fait ce choix, compare les performances entre versions et documente les commandes utiles au quotidien pour l'équipe.

9.7 LTS LTS en cours Sortie avr. 2026, support actif jusqu'en avr. 2031
8.4 LTS LTS précédente Sortie avr. 2024, support actif jusqu'en avr. 2029
8.0 Migration urgente Sortie avr. 2018, plus aucun correctif depuis avr. 2026

PRÉSENTATION

Qu'est-ce que MySQL ?

MySQL est un SGBDR (Système de Gestion de Bases de Données Relationnelles) open source créé en 1995 par Michael Widenius. Aujourd'hui maintenu par Oracle, il est utilisé par des millions d'applications dans le monde, des blogs WordPress aux systèmes transactionnels d'entreprise.

Son moteur de stockage InnoDB, devenu le défaut depuis MySQL 5.5, garantit les propriétés ACID : Atomicité, Cohérence, Isolation et Durabilité. Chaque transaction est soit entièrement appliquée, soit entièrement annulée, même en cas de crash serveur.

Chez SmartBooster, MySQL s'intègre dans notre stack via Doctrine ORM qui génère les requêtes SQL optimisées et gère les migrations de schéma depuis le code source PHP.

Logo MySQL

POURQUOI MYSQL

Ce qui en fait notre standard de base de données

MySQL est présent sur tous nos projets Symfony dès le premier jour. Voici pourquoi nous ne développons plus sans lui.

Base de référence de l'écosystème Symfony

MySQL est la base de données par défaut des projets Symfony chez SmartBooster. Doctrine ORM génère des requêtes optimisées pour MySQL et notre template symfony-docker intègre MySQL nativement dans la stack de développement.

Transactions ACID et fiabilité InnoDB

Le moteur InnoDB garantit des transactions atomiques, une cohérence des données après crash et des clés étrangères avec contraintes d'intégrité. Aucune donnée corrompue en cas de coupure réseau au milieu d'un INSERT groupé.

Performance indexée pour les applications métier

Les index composites, les index couvrants et le query optimizer MySQL s'intègrent avec Doctrine ORM. L'essentiel des gains de performance vient de la structure des index et des requêtes, pas de la version MySQL. C'est par là que commence notre audit de performance.

Image Docker officielle prête à l'emploi

L'image officielle mysql:8.4 est disponible sur Docker Hub et intégrée dans notre stack Docker standard. Démarrage en une commande, données persistées dans un volume nommé, configuration via variables d'environnement.

Migrations de schéma versionnées avec Doctrine

Les migrations Doctrine générées depuis les entités PHP créent des scripts SQL versionnés et rejouables. Chaque evolution du schéma est tracée dans Git et déployée automatiquement en CI avant chaque mise en production.

Deux versions LTS au choix

MySQL 8.4 LTS (support jusqu'en 2032) et MySQL 9.7 LTS sorti avril 2026 (support jusqu'en 2034). Pour les projets existants sur 8.4 : aucune migration forcée avant 2032. Pour les nouveaux projets, MySQL 9.7 LTS est le choix naturel.

PERFORMANCE

MySQL par version : gains de performance

MySQL publie régulièrement de nouvelles versions LTS et Innovation. Voici les gains de performance observés sur des applications web PHP/Symfony standards, mesurés via des benchmarks sysbench OLTP sur des charges mixtes lecture/écriture.

Saut de version Gain typique Explication
MySQL 5.6 → 5.7 Scalabilité (forte concurrence) Refonte de la scalabilité InnoDB en read-write : purge multi-threadée, pool de buffer mieux partitionné et statistiques adaptatives de l'optimiseur. Percona observe un net gain de débit à forte concurrence (plusieurs centaines de connexions simultanées). À faible concurrence en revanche, 5.7 n'apporte pas de gain et peut rester légèrement derrière 5.6, son exécution mono-thread étant plus lente.
MySQL 5.7 → 8.0 5.7 souvent plus rapide Contrairement à l'idée reçue, MySQL 8.0 n'est pas toujours plus rapide que 5.7. Sur un test sysbench à 24 threads, Percona mesure 5.7 plus rapide dans toutes les configurations : de 12 à 22 % avec les binary logs activés (cas standard pour la réplication), de 5 à 16 % sans. L'intérêt de migrer vers 8.0 est avant tout fonctionnel : CTE, Window Functions, roles, JSON enrichi et Atomic DDL.
MySQL 8.0 → 8.4 LTS Marginal (< 5 %) La version 8.4 LTS est une consolidation, pas une refonte de moteur. Les gains de performance sont anecdotiques sur les workloads applicatifs classiques. L'intérêt de la migration est exclusivement la sécurité et la garantie de support jusqu'en 2032, pas le débit.
MySQL 8.4 → 9.7 LTS +6.5 % (moyenne PGO)
+14.3 % (pic) 32 Go, 1 thread
MySQL 9.7 LTS est le premier binaire MySQL compilé avec le PGO (Profile-Guided Optimization) : le compilateur analyse les chemins d'exécution chauds lors d'un profiling réel, puis recompile l'ensemble du code en ciblant ces chemins. Percona mesure +6.5 % en moyenne sur sysbench OLTP, avec un pic à +14.3 % sur les configurations 32 Go (grand buffer pool) à faible concurrence. Le gain est le plus fort avec un grand buffer pool et s'amenuise sur les petites configurations mémoire et à très haute concurrence.

Les gains indiqués sont des ordres de grandeur issus de benchmarks sysbench OLTP et de mesures en conditions réelles sur des applications PHP/Symfony. Ils varient selon le ratio lecture/écriture, la taille des tables, la configuration du pool de buffer InnoDB et l'infrastructure. Ces chiffres ne se substituent pas à un profiling sur votre propre application. Sources : Percona : MySQL 5.7 read-write benchmarks, Percona : MySQL 8 n'est pas toujours plus rapide que 5.7, Percona : MySQL 9.7.0 PGO benchmark analysis, Percona : rapport 2026 MySQL ecosystem benchmark.

HISTORIQUE

Versions de MySQL

MySQL suit un double calendrier depuis 2024 : le canal LTS (Long Term Support, tous les 2-3 ans) pour la stabilité en production, et le canal Innovation (trimestriel) pour les nouvelles fonctionnalités. Voir les dates de fin de support officielles →

Chez SmartBooster, nous utilisons exclusivement des versions LTS en production. Les versions du canal Innovation (sorties trimestrielles : 8.1 à 8.3, 9.0 à 9.6) apportent les nouvelles fonctionnalités mais sans garantie de support long terme : leur description ci-dessous précise qu'elles ne sont pas des LTS et qu'elles sont rapidement éclipsées par la LTS suivante.

  • MySQL 26.7

    Support actif

    Juil. 2026

    aucune fin de support annoncée

    Première publication du versionnement calendaire : AA.M, soit juillet 2026. Canal Innovation, destiné aux équipes qui suivent les nouveautés au rythme trimestriel. Pour un logiciel métier, la LTS reste la cible.

    • Versionnement calendaire
      La 9.7 est la dernière version numérotée séquentiellement. Les suivantes prennent la forme AA.M : 26.7 pour juillet 2026, 27.7 pour juillet 2027. Une version LTS garde son numéro pendant toute sa durée de support.
    • Canal Innovation
      Publication destinée aux équipes qui veulent les dernières fonctionnalités et disposent de tests automatisés. Pour un projet métier en production, la LTS reste la cible.
  • MySQL 9.7 LTS

    Recommandée

    Avr. 2026

    support actif jusqu'en avr. 2031

    Deuxième version LTS d'Oracle depuis le passage au cycle LTS / Innovation, et la dernière à porter un numéro séquentiel. Elle consolide ce qui a été éprouvé dans les publications 9.0 à 9.6. Le choix naturel pour tout nouveau projet.

    • LTS : 5 ans de support Premier, 3 ans de plus en Extended
      Une série LTS suit la politique de support Oracle et ne reçoit que les correctifs nécessaires : aucune fonctionnalité n'est retirée en cours de série. C'est la cible pour un projet en production qui ne veut pas subir de changement de comportement.
    • Le canal Innovation consolidé
      La LTS reprend ce qui a été éprouvé dans les publications 9.0 à 9.6, dont le type VECTOR introduit en 9.0. Elle évite de suivre un rythme de publication trimestriel.
  • MySQL 9.0 → 9.6

    Obsolète depuis avr. 2026

    Juin 2024

    plus de correctifs depuis avr. 2026

    Versions du canal Innovation d'Oracle : sorties trimestrielles avec de nouvelles fonctionnalités sans garantie de support long terme. Cette branche non-LTS n'est plus maintenue depuis la sortie de 9.7 LTS, qui a intégré ses fonctionnalités stables.

    • Type VECTOR
      Type de colonne pour stocker des vecteurs. Utilisé pour les recherches de similarité (RAG, recommandations) sans extension tierce.
    • Programmes stockés en JavaScript, en Enterprise Edition
      CREATE FUNCTION ... LANGUAGE JAVASCRIPT permet d'écrire de la logique directement dans la base. Réservé à MySQL Enterprise Edition : indisponible sur la version Community, celle qu'utilisent la quasi-totalité des hébergements.
  • MySQL 8.4 LTS

    Support actif avr. 2029

    Avr. 2024

    support actif jusqu'en avr. 2029

    Première version LTS officielle d'Oracle pour MySQL 8.x, toujours en support actif. Recommandée pour les projets existants : elle ne force aucune migration à court terme.

    • Première LTS du modèle Oracle
      Correctifs de sécurité jusqu'en avril 2032. La cible pour un projet en production qui veut éviter les migrations fréquentes.
    • mysql_native_password désactivé par défaut
      L'ancien mécanisme d'authentification est désactivé : les comptes applicatifs créés avant 8.0 ne peuvent plus se connecter tant que le plugin n'est pas réactivé ou le compte migré. C'est le premier blocage rencontré lors d'une montée depuis 8.0.
    • Valeurs par défaut InnoDB revues
      Plusieurs variables système InnoDB changent de valeur par défaut, et des options dépréciées depuis 8.0 sont supprimées. Un audit de la configuration serveur est nécessaire avant la bascule.
  • MySQL 8.1 → 8.3

    Obsolète depuis avr. 2024

    Juin 2023

    plus de correctifs depuis avr. 2024

    Premières versions du nouveau canal Innovation d'Oracle, introduit avec la bifurcation de la branche MySQL 8.x. Ces versions trimestrielles non-LTS ont été éclipsées par la sortie de MySQL 8.4 LTS en avril 2024.

  • MySQL 8.0

    Obsolète depuis avr. 2026

    Avr. 2018

    plus de correctifs depuis avr. 2026

    Version majeure avec refonte de la gestion des droits, JSON enrichi et Window Functions. Avant le modèle LTS / Innovation, MySQL 8.0 a assuré un cycle de support inhabituellement long. La migration vers 8.4 LTS ou 9.7 LTS est aujourd'hui urgente.

    • Roles et gestion des droits
      Les roles MySQL permettent de regrouper des ensembles de privilèges et de les assigner à plusieurs utilisateurs. Remplace les scripts de GRANT laborieux.
    • Window Functions
      ROW_NUMBER(), RANK(), LAG(), LEAD()... Les fonctions analytiques permettent des agrégations glissantes et des classements sans sous-requêtes imbriquées complexes.
    • CTE (Common Table Expressions)
      Les expressions WITH améliorent la lisibilité des requêtes complexes. Les CTE récursives permettent de parcourir des structures arborescentes (catégories imbriquées, commentaires) en SQL natif.
    • Atomic DDL
      Les opérations DDL (CREATE, DROP, ALTER) sont atomiques et enregistrées dans le redo log. Plus de tables orphelines ou d'état incohérent après un crash pendant un ALTER TABLE en production.
    • UTF8MB4 par défaut
      L'encodage 4 octets devient le défaut, couvrant l'intégralité d'Unicode dont les emojis. Fin des truncations silencieuses sur les champs VARCHAR avec des caractères hors BMP.
  • MySQL 6 et 7

    Versions inexistantes

    Non publiés

    pas encore publiée

    MySQL 6.0 a été lancé en développement en 2007 puis abandonné après le rachat de MySQL AB par Sun Microsystems en 2008. Les fonctionnalités utiles ont été réintégrées dans la branche 5.x. MySQL 7.x est réservé à MySQL Cluster NDB, la variante distribuée de MySQL. Oracle a sauté directement à MySQL 8.0 en 2018 pour éviter toute ambiguïté de numérotation.

  • MySQL 5.7

    Obsolète depuis oct. 2023

    Oct. 2015

    plus de correctifs depuis oct. 2023

    Version qui a introduit le type JSON natif et amélioré InnoDB de façon significative. Tout projet encore sur MySQL 5.7 doit migrer immédiatement.

    • Type JSON natif
      Stockage optimisé et indexation de documents JSON directement dans InnoDB. Opérateurs ->, ->> et JSON_EXTRACT() pour accéder aux champs sans désérialiser.
    • Generated columns
      Colonnes virtuelles calculées depuis d'autres colonnes, indexables sans stockage physique (VIRTUAL) ou matérialisées (STORED). Utiles pour indexer un champ extrait d'un document JSON.
    • sys schema
      Base de données de diagnostic pré-installée avec des vues lisibles des tables performance_schema. Simplifie considérablement l'identification des requêtes lentes et des locks.
  • MySQL 5.6

    Obsolète

    Févr. 2013

    plus de correctifs depuis févr. 2021

    La version qui a modernisé InnoDB et introduit la réplication GTID. Elle ne doit plus être utilisée en production.

    • Réplication GTID
      Les Global Transaction Identifiers simplifient la gestion des architectures de réplication en identifiant chaque transaction de façon unique sur tous les noeuds.
    • Online DDL
      Les ALTER TABLE s'exécutent sans verrouiller complètement la table en lecture. Les migrations de schéma sur des tables volumineuses deviennent moins risquées en production.
    • Full-text search InnoDB
      La recherche full-text étendue nativement à InnoDB (auparavant réservée à MyISAM). Utilisable pour la recherche textuelle sans solution tierce pour les volumes modérés.
  • MySQL 5.5

    Obsolète

    Déc. 2010

    plus de correctifs depuis déc. 2018

    La version qui a fait d'InnoDB le moteur par défaut, remplaçant définitivement MyISAM pour les tables transactionnelles. Introduction de la semi-synchronous replication et du Performance Schema.

    • InnoDB moteur par défaut
      InnoDB remplace MyISAM comme moteur de stockage par défaut. Transactions ACID, clés étrangères et récupération sur crash deviennent le standard MySQL.
    • Performance Schema
      Nouvelle base de données de monitoring avec des tables d'instrumentation du serveur. Ancêtre du sys schema de MySQL 5.7, orienté DBA plutôt que développeur.
  • MySQL 5.0

    Obsolète

    Oct. 2005

    plus de correctifs depuis janv. 2012

    Version majeure qui a apporté les fonctionnalités attendues d'un SGBDR professionnel : procédures stockées, vues et déclencheurs.

    • Procédures stockées
      Logique métier encapsulée côté base de données, exécutable via CALL. Réduit les allers-retours réseau sur les traitements complexes.
    • Vues (Views)
      Tables virtuelles basées sur des requêtes SELECT, permettant de simplifier l'accès aux données et de gérer les droits par vue.
    • Triggers
      Déclencheurs exécutés automatiquement sur INSERT, UPDATE ou DELETE. Utilisés pour l'audit, la dénormalisation automatique ou les contraintes complexes.
  • MySQL 4.0 / 4.1

    Obsolète

    2003-2004

    plus de correctifs depuis déc. 2009

    Introduction des sous-requêtes (4.1), de la commande UNION et de l'intégration optionnelle d'InnoDB. Première étape vers un SGBDR complet.

  • MySQL 3.23

    Obsolète

    2001

    plus de correctifs depuis déc. 2006

    Première version avec le support optionnel d'InnoDB et de MyISAM. Introduction de la réplication master-slave. Début de la popularisation sur les hébergements mutualisés.

Rester sur une version active de MySQL, c'est bénéficier des correctifs de sécurité et de la compatibilité avec les dernières versions de PHP et Doctrine ORM. Une version obsolète expose votre application à des vulnérabilités non corrigées et bloque les mises à jour de vos dépendances Symfony.

NOTRE USAGE

Comment nous utilisons MySQL chez SmartBooster

MySQL est au coeur de tous nos projets Symfony. Voici les trois piliers qui structurent notre usage au quotidien.

Entités et Repositories Doctrine

Chaque table MySQL correspond à une entité PHP annotée avec les attributs Doctrine. Le Repository associé centralise toutes les requêtes sur cette table. Cette séparation garantit que la logique de persistance ne se dilue pas dans les controllers ou les services.

Migrations versionnées (up/down)

Chaque modification de schéma génère un fichier de migration avec une méthode up() (applique le changement) et down() (annule le changement). Ces fichiers sont versionnés dans Git et déployés automatiquement en CI : la structure de la base reste toujours synchronisée avec le code, sans intervention manuelle.

Fixtures de développement

Des fixtures standardisées alimentent chaque environnement de développement avec des données cohérentes et reproductibles. Chaque développeur qui clone le projet rejoue les fixtures et retrouve immédiatement un état de base connu, sans dépendre d'un dump personnel.

MOTEURS DE STOCKAGE

InnoDB, MyISAM et les autres moteurs MySQL

MySQL supporte plusieurs moteurs de stockage interchangeables par table. En pratique, InnoDB est le seul choix pertinent pour les applications Symfony en production depuis MySQL 5.5 (2010).

Moteur Transactions ACID Clés étrangères Usage
InnoDB Oui Oui Moteur par défaut depuis MySQL 5.5. Le seul choix pour toute table applicative avec Symfony/Doctrine : transactions, contraintes d'intégrité et récupération après crash.
MyISAM Non Non Moteur historique, plus rapide en lecture pure sur les très vieilles versions MySQL, mais sans sécurité transactionnelle. A éviter sur tout nouveau projet.
MEMORY Non Non Données stockées en RAM, volatiles au redémarrage. Utile pour des tables temporaires dans des procédures stockées ou des caches intermédiaires.
ARCHIVE Non Non Stockage compressé en append-only, sans mise à jour ni suppression possible. Pour des journaux d'audit à très fort volume qu'on ne modifie jamais.

Doctrine ORM génère du DDL ((Data Definition Language) InnoDB par défaut. Si une table existante tourne sous MyISAM, une migration ALTER TABLE ... ENGINE=InnoDB suffit à la convertir.

ALTERNATIVES

MariaDB et Percona Server : les variantes de MySQL

MySQL officiel n'est pas la seule distribution. MariaDB et Percona Server sont deux variantes majeures, compatibles au niveau du protocole, mais avec des différences importantes à connaître avant de choisir son hébergeur.

Distribution Éditeur Compatibilité MySQL Particularité
MySQL officiel Oracle Référence Canal LTS et Innovation, support Oracle garanti. Version officielle de MySQL.
MariaDB MariaDB Foundation Haute mais diverge Fork créé en 2009 par Michael Widenius (le créateur originel de MySQL) après le rachat par Oracle. Diverge de plus en plus depuis MySQL 8.0 : certaines fonctionnalités récentes ne sont pas portées. Présent sur de nombreux hébergements mutualisés sous le label "MySQL" : vérifier avec SELECT VERSION().
Percona Server Percona 100% compatible Distribution MySQL avec des patches de performance, des outils de monitoring avancés et Percona XtraBackup pour les sauvegardes à chaud sans downtime. Binaire drop-in compatible avec MySQL officiel. Clever Cloud utilise Percona Server pour son add-on MySQL.

Source : Documentation Clever Cloud : add-on MySQL (Percona Server) . Les dumps SQL (mysqldump) et Doctrine ORM fonctionnent de façon identique entre MySQL officiel, MariaDB et Percona Server.

BONNES PRATIQUES

Performance MySQL : les bases pour un développeur Symfony

Pas besoin d'être DBA pour éviter les pièges classiques. Ces règles couvrent 90 % des problèmes de performance rencontrés sur des applications Symfony en production.

A faire

Utiliser EXPLAIN avant d'optimiser

EXPLAIN SELECT ... révèle le plan d'exécution : type ALL signifie un full table scan (mauvais), ref ou range signifie qu'un index est utilisé (bon). MySQL 8.0+ : EXPLAIN ANALYZE donne les vrais temps d'exécution mesurés, pas juste le plan estimé.

A faire

Indexer les colonnes utilisées en WHERE et JOIN

Un index manquant force un full table scan sur chaque requête. Les colonnes de clé étrangère, de filtre (status, type) et de tri (created_at) doivent systématiquement être indexées. Avec Doctrine, l'attribut #[ORM\Index] sur l'entité génère l'index via la migration.

Erreur classique

Le N+1 en Doctrine

Charger 100 entités puis accéder à leur relation en lazy loading génère 101 requêtes MySQL. Solution : ->leftJoin('u.posts', 'p')->addSelect('p') dans le QueryBuilder pour charger les relations en une seule requête avec un JOIN.

Erreur classique

Enrober une colonne indexée dans une fonction

WHERE YEAR(created_at) = 2024 empêche l'utilisation de l'index sur created_at. Réécrire en plage de dates : WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01' pour que l'index B-tree soit exploité.

Erreur classique

SELECT * sur des entités volumineuses

Doctrine charge par défaut toutes les colonnes. Sur une liste, utiliser ->select('u.id, u.name') dans le QueryBuilder pour éviter de transférer des colonnes TEXT ou BLOB inutilement sur chaque ligne.

Erreur classique

LIKE '%texte%' ne peut pas utiliser un index

Un LIKE '%mot%' avec un joker en début de motif force un full table scan. Pour la recherche textuelle, préférer un index FULLTEXT avec MATCH AGAINST, ou Blackfire pour identifier les requêtes problématiques sur un volume réel.

STRATÉGIE D'INDEXATION

Comment choisir, positionner et nettoyer ses index

Un index manquant ralentit les lectures. Un index inutile ralentit les écritures. La bonne stratégie se construit sur les requêtes réelles, pas sur l'intuition.

1. Détecter avec le slow query log

Activer le slow query log pour enregistrer toutes les requêtes dépassant un seuil. C'est la source de vérité : on indexe ce qui est lent en production, pas ce qu'on suppose lent.

-- Activer en session (sans redémarrer MySQL)
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 0.5; -- en secondes
SET GLOBAL log_queries_not_using_indexes = 'ON';

L'outil pt-query-digest (Percona Toolkit) agrège le log et liste les requêtes les plus coûteuses en premier.

2. Radiographier avec EXPLAIN

EXPLAIN affiche le plan d'exécution sans exécuter la requête. La colonne type révèle si un index est utilisé.

type Lecture
const / ref Index utilisé, peu de lignes lues
range Index sur une plage (BETWEEN, >, <)
ALL Full Table Scan : aucun index utilisé

EXPLAIN ANALYZE exécute réellement la requête et affiche les temps mesurés (MySQL 8.0+).

3. Quelles colonnes indexer en priorité

L'ordre de priorité pour les colonnes candidates à l'indexation :

  1. 1 WHERE : la colonne filtrée est la candidate principale. Plus elle est sélective, plus l'index est efficace.
  2. 2 JOIN ON : les colonnes de jointure sans index forcent un scan complet de la table jointe.
  3. 3 ORDER BY : un index sur la colonne de tri évite le tri en mémoire (filesort dans EXPLAIN).
  4. ✕ SELECT uniquement : une colonne présente seulement dans le SELECT ne bénéficie pas d'un index.

4. Index composite : la règle du préfixe gauche

Un index composite (status, created_at) sert les requêtes qui filtrent sur le préfixe gauche des colonnes.

✓ WHERE status = 'published' utilise l'index
✓ WHERE status = 'x' AND created_at > '2025' utilise l'index
✕ WHERE created_at > '2025' pas d'index

Ordre dans l'index : colonne la plus sélective en premier. Tester avec EXPLAIN ANALYZE sur les données réelles avant de décider.

5. Pas toutes les combinaisons : le coût des index

Chaque index doit être mis à jour à chaque INSERT, UPDATE et DELETE. Un index a un coût en écriture, pas seulement en espace disque. Indexer seulement les requêtes qui apparaissent dans le slow query log ou dans les endpoints critiques mesurés.

Workflow recommandé

  1. 1. Activer le slow query log en production
  2. 2. Identifier les requêtes lentes avec pt-query-digest
  3. 3. EXPLAIN sur chaque requête problématique
  4. 4. Ajouter un index ciblé (Doctrine : #[ORM\Index])
  5. 5. Vérifier avec EXPLAIN ANALYZE

6. Nettoyer les index inutilisés

MySQL enregistre les statistiques d'utilisation des index dans performance_schema. La vue sys.schema_unused_indexes liste tous les index jamais utilisés depuis le dernier démarrage du serveur.

-- Index jamais utilisés depuis le démarrage
SELECT object_schema, object_name, index_name
FROM sys.schema_unused_indexes
WHERE object_schema = 'ma_base'
ORDER BY object_name;

-- Confirmer le non-usage avec le compteur brut
SELECT object_schema, object_name, index_name, count_star
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE index_name IS NOT NULL
  AND count_star = 0
  AND object_schema = 'ma_base'
ORDER BY object_name;

Précaution : les statistiques sont remises à zéro au redémarrage de MySQL. Observer sur une période représentative (idéalement plusieurs semaines en production) avant de supprimer un index.

Cas particulier : les index UNIQUE ne doivent jamais être supprimés sans vérifier les contraintes d'intégrité, même s'ils n'apparaissent pas dans les lectures.

BONNES PRATIQUES & DOCUMENTATION

Nos conventions MySQL

Documentation de référence pour l'équipe SmartBooster.

Se connecter à MySQL depuis un conteneur Docker

Dans nos projets Symfony, MySQL tourne dans un conteneur Docker. Pour ouvrir une session MySQL interactive depuis le poste de développement :

# Connexion au conteneur mysql, puis lancement du client MySQL
docker compose exec mysql mysql -u root -p

# Connexion directe en une commande (mot de passe via variable d'environnement)
docker compose exec mysql mysql -u app -papp app

Le troisième argument (app) est le nom de la base de données. Les identifiants sont définis dans le docker-compose.yml via les variables MYSQL_USER, MYSQL_PASSWORD et MYSQL_DATABASE.

Commandes MySQL utiles au quotidien

Une fois connecté au client MySQL, les commandes suivantes couvrent l'essentiel des besoins quotidiens :

Commande Explication
SHOW DATABASES; Liste toutes les bases de données accessibles avec l'utilisateur courant
USE nom_base; Sélectionne la base de données active pour les requêtes suivantes
SHOW TABLES; Liste les tables de la base de données active
DESCRIBE nom_table; Affiche la structure d'une table (colonnes, types, clés, valeurs par défaut)
SHOW CREATE TABLE nom_table\G Affiche le DDL complet de la table, utile pour auditer les index
SHOW FULL PROCESSLIST; Liste les requêtes en cours d'exécution, pour identifier les requêtes bloquantes
SHOW STATUS LIKE 'Slow%'; Compteurs de requêtes lentes depuis le démarrage du serveur

Analyser les requêtes lentes avec EXPLAIN

EXPLAIN est la commande clé pour diagnostiquer les problèmes de performance. Elle affiche le plan d'exécution choisi par l'optimiseur MySQL pour une requête donnée.

EXPLAIN SELECT * FROM commande WHERE client_id = 42 AND statut = 'en_cours';

Les colonnes à surveiller en priorité :

  • type : ALL signifie un full table scan (problème). ref, range ou const indiquent qu'un index est utilisé.
  • key : nom de l'index utilisé. NULL = pas d'index = problème.
  • rows : estimation du nombre de lignes scannées. Plus ce chiffre est proche du résultat attendu, mieux c'est.
  • Extra : Using filesort ou Using temporary sont des signaux de requêtes à optimiser.

EXPLAIN ANALYZE (MySQL 8.0+) exécute réellement la requête et retourne les temps d'exécution réels par étape, beaucoup plus précis que l'estimation de EXPLAIN seul.

Gestion des dumps SQL

Exporter une base de données (depuis le conteneur) :

docker compose exec mysql mysqldump -u root -p app > dump.sql

Exporter uniquement la structure (sans les données) :

docker compose exec mysql mysqldump -u root -p --no-data app > structure.sql

Restaurer un dump dans un conteneur :

docker compose exec -T mysql mysql -u root -p app < dump.sql

Le flag -T (sans TTY) est nécessaire pour la redirection stdin dans docker compose exec.

Commandes Doctrine au quotidien (Symfony)

Les migrations de schéma passent par Doctrine et non par MySQL directement. Les commandes bin/console à connaître :

# Générer une migration depuis les changements d'entités PHP
php bin/console doctrine:migrations:diff

# Appliquer les migrations en attente (à lancer avant chaque mise en production)
php bin/console doctrine:migrations:migrate

# Voir le statut des migrations (lesquelles sont appliquées, lesquelles sont en attente)
php bin/console doctrine:migrations:status

# Valider que le schéma en base correspond aux entités PHP (pratique en CI)
php bin/console doctrine:schema:validate

En développement local, doctrine:schema:update --force peut être utilisé pour synchroniser rapidement le schéma sans passer par les migrations. En environnement de recette ou production, utiliser exclusivement doctrine:migrations:migrate.

Requête pour avoir la taille des bases de données

SELECT
    table_schema AS "Base de données",
    ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS "Taille (Mo)"
FROM
    information_schema.TABLES
GROUP BY
    table_schema
ORDER BY
    SUM(data_length + index_length) DESC;

Pour aller plus loin

Documentation utile

Pour aller plus loin

Approfondir votre réflexion

Vous avez un projet ?

Contactez-nous pour savoir comment nous pouvons vous aider.