# Un multisite de 500 sites sature max_connections aux inscriptions

> Retour d'expérience sur un pic de connexions MySQL provoqué par une campagne d'inscription simultanée sur 500 sites, résolu par un pool de connexions partagé.

- Auteur : Clément Hadrot
- Publié le : 2024-06-29
- Mis à jour le : 2024-06-29
- Catégorie : Performance
- URL : https://wpmoderne.dev.wordpress-developpement.fr/performance/multisite-500-sites-pool-connexions-mysql/

## L’essentiel

- Chaque site d'un multisite ouvre potentiellement sa propre connexion MySQL simultanée
- max_connections fixe une limite dure indépendante du nombre de sites du réseau
- Un pool de connexions partagé absorbe les pics sans augmenter la mémoire allouée à MySQL

« Too many connections » — ce message MySQL, remonté dans les journaux d'erreur de plusieurs sites du réseau en même temps, a marqué le début d'un incident déclenché par une campagne d'inscription périscolaire ouverte simultanément sur les cinq cents sites d'un multisite WordPress mutualisé par une collectivité pour ses établissements scolaires.

Chaque établissement disposait de son propre site dans le réseau multisite, partageant la même base de données physique mais avec des tables préfixées séparément (`wp_2_options`, `wp_3_options`, etc.). L'ouverture simultanée du formulaire d'inscription à 8h précises, heure officielle de lancement communiquée aux familles, a concentré un afflux de connexions sur une fenêtre de quelques minutes.

## Le mécanisme qui a mené à la saturation

Un multisite WordPress partage la même instance MySQL entre tous ses sites, mais chaque requête HTTP traitée par PHP-FPM ouvre sa propre connexion à la base de données, indépendamment du site du réseau qu'elle sert. Avec plusieurs centaines de familles connectées simultanément à des sites différents du réseau, chacune déclenchant plusieurs requêtes Ajax de vérification de disponibilité de créneau, le nombre de connexions simultanées a grimpé jusqu'à 512, dépassant la limite `max_connections` fixée à 400 dans la configuration MySQL du serveur.

Une fois cette limite atteinte, MySQL refuse purement et simplement toute nouvelle connexion, y compris pour des sites du réseau qui n'étaient pas directement concernés par la campagne d'inscription, provoquant un effet de bord sur l'ensemble du multisite plutôt qu'un ralentissement localisé.

## Pourquoi augmenter simplement max_connections n'était pas suffisant

> L'essentiel à retenir : Chaque site d'un multisite ouvre potentiellement sa propre connexion MySQL simultanée ; max_connections fixe une limite dure indépendante du nombre de sites du réseau ; Un pool de connexions partagé absorbe les pics sans augmenter la mémoire allouée à MySQL

Le réflexe immédiat, augmenter `max_connections` dans `my.cnf`, a été envisagé puis écarté après calcul : chaque connexion MySQL consomme de la mémoire pour ses buffers de travail (tri, jointures temporaires), de l'ordre de plusieurs mégaoctets selon les réglages de `sort_buffer_size` et `join_buffer_size`. Multiplier la limite par deux ou trois aurait risqué de faire déborder la mémoire disponible du serveur en cas de nouveau pic, transformant un refus de connexion propre en un plantage du service MySQL entier.

## La solution retenue : un pool de connexions partagé

ProxySQL a été installé en frontal de l'instance MySQL, avec un pool de connexions limité côté base de données mais capable de mettre en attente les requêtes excédentaires plutôt que de les rejeter brutalement :

```
# Extrait de configuration ProxySQL (mysql_servers et mysql_query_rules)
INSERT INTO mysql_servers(hostgroup_id, hostname, port, max_connections)
VALUES (0, '127.0.0.1', 3306, 350);

UPDATE global_variables SET variable_value='600'
WHERE variable_name='mysql-max_connections';
```

Ce réglage autorise jusqu'à 600 connexions côté applicatif (PHP-FPM), tout en limitant à 350 le nombre de connexions réellement ouvertes vers MySQL. Les requêtes excédentaires sont mises en file d'attente par ProxySQL, avec une latence supplémentaire de quelques dizaines à quelques centaines de millisecondes en cas de pic, plutôt qu'un rejet immédiat par un message d'erreur visible du visiteur.

## Résultat lors de la campagne suivante

| Mesure | Sans ProxySQL | Avec ProxySQL |
| --- | --- | --- |
| Connexions MySQL simultanées au pic | 512 (rejets au-delà de 400) | 348 (plafonné, file d'attente ProxySQL) |
| Erreurs « Too many connections » | Nombreuses, sur plusieurs sites | 0 |
| Latence médiane pendant le pic | Non mesurable (erreurs) | 640 ms |

### Prévention pour les campagnes suivantes

- Un plafond de connexions MySQL doit être dimensionné en fonction de la mémoire disponible, jamais augmenté sans recalcul de la consommation mémoire totale possible.
- Un proxy de connexions absorbe un pic ponctuel sans changer la capacité réelle de la base ; il ne dispense pas d'un dimensionnement raisonnable en amont.
- Communiquer une heure de lancement précise à un grand nombre de familles concentre mécaniquement la charge ; étaler l'ouverture par ordre alphabétique d'établissement a été envisagé comme mesure complémentaire pour les campagnes suivantes.

> Un pic de charge prévisible mérite un plan de charge, pas seulement une limite technique relevée au dernier moment.

## En résumé

Ce dossier ne traite pas de la sécurité des formulaires d'inscription eux-mêmes, uniquement de la couche de connexion à la base de données mise sous tension par la concentration du trafic. Le pool de connexions partagé via ProxySQL a permis d'absorber un pic prévisible, structurel à ce type d'usage multisite, sans que chaque établissement scolaire n'ait eu à modifier quoi que ce soit à son propre site.
