Cet article couvre quatre axes que les équipes ops et tech leads ont intérêt à auditer avant de creuser plus loin : les connexions (persistantes ou non), l'encodage bout-en-bout en utf8mb4, les niveaux d'isolation des transactions, et les timeouts. Pour chacun : les symptômes à reconnaître, les paramètres à vérifier et les recommandations concrètes à appliquer dans settings.php et dans la configuration serveur.
Connexions : persistantes ou non ?
PHP peut ouvrir une nouvelle connexion MySQL à chaque requête HTTP, ou réutiliser une connexion déjà ouverte par le même processus. Les connexions persistantes ont l'air attrayantes : moins de surcharge d'initialisation. En pratique, elles posent plus de problèmes qu'elles n'en résolvent sur un stack Drupal classique.
Drupal 9 et 10 utilisent la classe de base de données PDO de PHP. Par défaut, les connexions persistantes sont désactivées. Le problème central : une connexion persistante garde son état entre deux requêtes HTTP. Si une transaction est laissée ouverte par inadvertance, la connexion suivante hérite de cet état. Résultat : des verrous qui ne se libèrent pas, des données partiellement écrites.
Avec PHP-FPM, plusieurs workers tournent en parallèle. Chaque worker maintient sa propre connexion persistante. Sur un serveur avec 50 workers, MySQL se retrouve avec 50 connexions ouvertes en permanence. La valeur max_connections de MySQL se consomme rapidement.
Recommandation : garder PDO::ATTR_PERSISTENT à FALSE (valeur par défaut). Si le coût d'ouverture de connexion est un problème mesuré, utiliser un proxy de connexion comme ProxySQL, qui mutualise les connexions proprement sans les risques d'état résiduel.
Encodage : utf8mb4 bout-en-bout
MySQL a longtemps appelé utf8 un jeu de caractères qui n'est en réalité qu'un sous-ensemble de l'UTF-8 réel : il gère les caractères sur 1 à 3 octets, mais pas ceux sur 4 octets (emojis, certains idéogrammes). Le vrai UTF-8 complet, c'est utf8mb4.
Symptômes d'un encodage mal configuré : les emojis disparaissent à l'insertion ou déclenchent une erreur Incorrect string value ; après import d'un dump SQL, des caractères accentués s'affichent en é au lieu de é.
L'encodage doit être cohérent à chaque maillon : serveur MySQL, base de données, tables, colonnes, et connexion PHP. Côté serveur, dans my.cnf :
character-set-server = utf8mb4collation-server = utf8mb4_unicode_ci
Côté Drupal, dans settings.php, ajouter les clés charset et collation dans la définition de la base de données. Sans ces deux clés, Drupal laisse MySQL décider de l'encodage de la connexion.
Pour diagnostiquer les tables non converties : SELECT table_name, table_collation FROM information_schema.tables WHERE table_schema = 'drupal' AND table_collation NOT LIKE 'utf8mb4%'.
Niveaux d'isolation des transactions
MySQL propose quatre niveaux d'isolation. Le défaut d'InnoDB est REPEATABLE READ. Ce niveau convient à la plupart des cas, mais il génère des gap locks qui peuvent provoquer des deadlocks sur des opérations d'insertion concurrentes — par exemple, plusieurs processus Cron qui écrivent dans la table watchdog ou queue simultanément.
READ COMMITTED réduit les verrous et élimine la plupart des deadlocks liés aux gap locks. C'est le niveau recommandé pour les applications à fort taux d'écriture concurrente. Pour l'appliquer à la connexion Drupal sans toucher au paramètre global du serveur :
$databases['default']['default']['init_commands'] = [
'isolation' => 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED',
];
Si des deadlocks reviennent régulièrement, SHOW ENGINE INNODB STATUS\G affiche le dernier deadlock détecté : les deux transactions, les tables et lignes verrouillées, et laquelle a été rollbackée automatiquement.
Timeouts : éviter les connexions et requêtes fantômes
Un timeout mal calibré crée deux problèmes opposés : trop court, il ferme des connexions légitimes en cours d'exécution ; trop long, il accumule des connexions mortes qui consomment des ressources.
wait_timeout : durée pendant laquelle MySQL attend une activité sur une connexion non interactive. Par défaut : 28 800 secondes (8 heures). Sur un serveur web, 60 à 300 secondes suffisent.
innodb_lock_wait_timeout : durée pendant laquelle InnoDB attend qu'un verrou soit libéré. Par défaut : 50 secondes. Réduire à 10-15 secondes évite que des processus bloqués monopolisent les connexions.
max_execution_time (MySQL 5.7.4+) : durée maximale d'exécution d'une requête SELECT, en millisecondes. Une valeur de 10 000 à 30 000 ms permet de tuer automatiquement les requêtes lentes sans intervention manuelle.
Checklist récapitulative
- Connexions :
PDO::ATTR_PERSISTENTdésactivé,wait_timeoutentre 60 et 300 secondes. - Encodage :
character-set-server = utf8mb4dansmy.cnf,charsetetcollationdanssettings.php, toutes les tables vérifiées viainformation_schema. - Isolation :
READ COMMITTEDviainit_commandspour les sites à forte écriture concurrente, vérification régulière deSHOW ENGINE INNODB STATUS. - Timeouts :
innodb_lock_wait_timeoutà 10-15 secondes,max_execution_timeà 10 000-30 000 ms.
Ces réglages couvrent la base. Un audit ciblé permet souvent de stabiliser le comportement en quelques heures, avant d'engager une refactorisation du code.