En cas de pics de trafic, Database Connection Saturation bloque les nouvelles requêtes parce que MySQL-et que WordPress n'obtient plus de slot. Je te montre de manière pratique comment MySQL protège contre les surcharges, réduit de manière mesurable les goulots d'étranglement et maintient des temps de réponse stables même en cas de charge élevée.
Points centraux
- causes : Trop peu de connexions, des requêtes lentes, des fuites.
- Diagnostic : Liste des processus, variables d'état, journal lent.
- Tuning : max_connections, cache de thread, timeouts.
- Décharge : Mise en commun, mise en cache, index.
- Mise à l'échelle : Read-Replicas, mise à l'échelle automatique.
Que signifie concrètement la saturation des connexions dans MySQL ?
Chaque demande entrante nécessite une Connexion, Et lorsque tous les slots sont occupés, les nouvelles connexions s'accumulent dans le backlog des sockets ou échouent avec des messages d'erreur. Dans ces moments-là, je vois souvent l'erreur typique „Too many connections“, parce que l'application n'a pas besoin de connexions libres. Fils de discussion alors que MySQL n'accepte plus rien. Ce qui est décisif, c'est le nombre de travailleurs PHP simultanés qui demandent une connexion en même temps et la durée pendant laquelle certaines requêtes restent ouvertes, car cela pousse la charge de travail à la saturation. Dans la pratique, j'utilise une formule simple : les web-workers simultanés multipliés par la durée moyenne des requêtes donnent la pression sur le pool, qui atteint alors rapidement le seuil de saturation. hébergement bottleneck est mis en évidence. Pour une entrée en matière structurée, il vaut la peine de jeter un coup d'œil sur Comprendre les limites de connexion, Il est important que la configuration et l'application soient compatibles.
Déclencheurs typiques en cas de trafic élevé
Plus de visiteurs signifie plus de visites simultanées Sessions, Plus une requête est longue, plus la connexion reste bloquée longtemps. Les longues lectures dues à l'absence d'index, les files d'attente de verrouillage dues à des écritures concurrentes et les fuites de connexion dans le code se combinent rapidement pour aboutir à une Saturation. Dans les environnements partagés, l'hébergeur limite souvent sévèrement le nombre de connexions par compte, ce qui génère brusquement des erreurs de 500 en cas de charge. De plus, les cronjobs, les crawlers et les admin-backends aggravent la situation en même temps, car ils sont en concurrence pour les slots dans le même pool. C'est pourquoi je prévois des marges de sécurité pour les limites, je surveille les pics de manière ciblée et je maintiens systématiquement les temps d'exécution des requêtes de l'ordre de la seconde en dessous de 0. Contrôle.
Reconnaître à temps les signes avant-coureurs
Je fais d'abord attention aux temps de chargement erratiques, parce que des temps de chargement croissants peuvent entraîner des problèmes. TTFB-me montrent très tôt que les connexions se font rares. Des messages comme „Error establishing a database connection“ ou „Too many connections“ marquent déjà le point où le pool est plein et où les requêtes échouent. Dans la liste des processus apparaissent alors de nombreuses entrées „Sleep“ ou „Waiting for table metadata lock“, ce qui indique des situations de verrouillage malheureuses ou trop de connexions à vide. Je vérifie en parallèle les délais d'attente dans l'application, car des limites serrées augmentent la visibilité des erreurs et génèrent de fausses alertes, tandis que des valeurs généreuses masquent les problèmes ; tu trouveras plus d'informations sur les causes et les pistes de vérification sous Timeouts de la base de données. Enfin, une courbe des fils connectés par rapport à la valeur maximale reste utile, car elle me permet de déterminer les derniers points de pourcentage avant la Saturation clairement.
Diagnostic : procéder étape par étape
Je commence toujours le diagnostic par le journal d'erreurs, car les erreurs récurrentes sont souvent liées à des problèmes de sécurité. Erreur Les problèmes de connexion y apparaissent directement. Ensuite, j'analyse la liste complète des processus, j'identifie les longues requêtes et je vérifie si elles sont bloquées ou si elles lisent lentement. Les variables d'état telles que Threads_connected, Threads_running et Max_used_connections me fournissent des points de mesure objectifs par rapport à la limite fixée, ce qui me permet de séparer les heures de pointe de la charge permanente. Ensuite, j'active le journal de requête lent avec un seuil modéré pour rendre visibles les déclarations vraiment coûteuses au lieu de m'attarder sur des pics aléatoires. Enfin, j'utilise EXPLAIN et je regarde les éventuels scans de tables complètes, les index manquants ainsi que les mauvaises stratégies de jointure, qui peuvent entraîner des erreurs ouvertes. Connections s'attacher longtemps.
Les chiffres clés du tuning en un coup d'œil
Avant de modifier les valeurs, je place le cadre au-dessus de la mémoire, Fils de discussion et la charge de travail, afin que MySQL ne glisse pas vers le swapping. J'utilise des valeurs de départ simples, je mesure les effets et j'affine par petites étapes plutôt que par grands sauts. Il reste important de vérifier la somme des tampons par connexion et des tampons globaux par rapport à la RAM disponible, afin qu'il reste des réserves libres pour les caches du système d'exploitation. J'évalue toujours chaque modification de la limite en même temps que la durée des requêtes et la gestion du pool, car plus de connexions ne suffit pas si les requêtes durent trop longtemps. Je résume le tableau suivant en tant qu'outil de référence rapide et j'y place des marqueurs pour les valeurs de départ et les mesures typiques que je garde toujours à l'œil dans le cadre de la surveillance, afin d'éviter les goulots d'étranglement. tôt de l'aborder.
| Réglage | Effet | Grandeur de mesure | Valeur de départ typique | Remarque |
|---|---|---|---|---|
| max_connections | Limite de la simultanéité Clients | Max_used_connections | 300-800 | N'augmenter que si la RAM est suffisante |
| thread_cache_size | Réduit les coûts de Fils de discussion | Threads_created | 128-512 | Si Threads_created augmente rapidement, augmenter la valeur |
| wait_timeout | Ferme les sites inactifs Sessions | Threads_connectés | 30-90 s | Plus court, il évite les blocages au ralenti |
| innodb_buffer_pool_size | Accélère la lecture et l'écriture Write-accès | Buffer Pool Hit Ratio | 50-70% RAM | S'adapter à la charge productive |
| max_allowed_packet | Permet de plus grandes Paquets | Erreur dans le journal d'erreurs | 64-256 MO | Ne soulever qu'en cas de besoin |
Configuration : régler MySQL pour la charge de pointe
J'adapte d'abord les limites centrales de manière dosée, car plus de Connections consomment également plus de RAM par connexion et que des effets secondaires peuvent se manifester. Un plan conservateur augmente progressivement max_connections, donne de l'air au cache des threads et raccourcit les délais d'attente afin que les sessions dormantes n'encombrent pas le pool. Avant chaque modification, je calcule la somme des tampons per-thread et des tampons globaux par rapport à la mémoire réellement disponible, afin d'éviter que des tempêtes de swap ne fassent grimper la latence. Ensuite, je vérifie si Max_used_connections touche régulièrement la nouvelle limite et si Threads_running est en corrélation avec le trafic au lieu de rester durablement élevé. Cette base rend les pics de charge gérables et ouvre la voie à des mesures supplémentaires contre Saturation.
[mysqld]
max_connections = 600
thread_cache_size = 256
wait_timeout = 60
interactive_timeout = 60
innodb_buffer_pool_size = 12G
innodb_flush_log_at_trx_commit = 1
Utiliser correctement le pool de connexion
Le pooling réduit les coûts d'établissement de connexion et dissocie les threads d'application de l'infrastructure. MySQL-ce qui permet de retarder la saturation. J'utilise pour cela un proxy de connexion, je limite sévèrement les connexions backend et je laisse le proxy mettre en mémoire tampon les demandes jusqu'à ce que des slots soient libérés. Dans les piles PHP, je me tiens à l'écart des connexions persistantes incontrôlées et j'utilise à la place un pool clairement configuré qui respecte les limites supérieures. Il reste important d'avoir un délai d'inactivité propre dans le pool, afin d'éviter que des dormeurs ne dévorent le pool backend et que des demandes ne restent bloquées sur le proxy. Pour une approche pratique plus approfondie, un guide compact sur les Pooling de connexions, Le système de gestion de l'application (GAI) est un système de gestion de l'application qui combine de manière cohérente les limites, les délais d'attente et les comportements de reprise afin que l'application soit stable. mis à l'échelle.
Des stratégies de mise en cache qui soulagent vraiment
Je retire du travail à la base de données en obtenant des résultats supérieurs à la DB et ainsi réduire la demande de connexion. Les caches de pages répondent aux accès anonymes sans requête, les caches d'objets conservent les options et métadonnées fréquentes dans la RAM et les stratégies transitoires lissent la charge d'écriture. Il est important de définir clairement les clés de cache, d'invalider au lieu de vider la surface et de choisir les TTL de manière à augmenter les taux de réussite sans risquer de rendre le contenu obsolète. Pour WordPress, j'utilise des caches d'objets dédiés avec Redis ou Memcached, car le taux de réussite pour la navigation, la page d'accueil et les catégories augmente rapidement de manière significative. Dès que j'augmente visiblement les occurrences du cache, Max_used_connections et Threads_running diminuent sensiblement, ce qui Saturation réduit.
Optimiser SQL et Schema
Je vérifie chaque requête lente avec EXPLAIN, car un manque de Index est souvent la véritable cause des exécutions de plusieurs minutes. Les index sélectifs sur les colonnes WHERE et JOIN transforment les analyses de tableaux complets en lectures d'index rapides, ce qui permet de supprimer les chaînes de verrouillage. Je simplifie les requêtes, je supprime les colonnes inutiles dans les listes SELECT et je divise les grands processus en étapes plus courtes, qui nécessitent moins de connexions longues. Pour WordPress, il vaut la peine de jeter un coup d'œil aux options d'autoload et aux plugins Chatty, dont l'accès permanent remplit le pool, bien qu'aucune page ne soit visiblement rendue plus rapidement. Des modifications DDL propres avec des fenêtres de maintenance courtes empêchent en outre les longs verrous de métadonnées qui, sinon, sont appelés „Waiting for table metadata lock“. Liste des processus boucher.
Mise à l'échelle : verticale, horizontale et Read-Replicas
Si le réglage et la mise en cache fonctionnent, je vérifierai le levier suivant : Mise à l'échelle sur plus de RAM et de CPU ou sur plusieurs nœuds de base de données. Les étapes verticales donnent à MySQL un buffer pool plus grand et plus de threads, ce qui permet aux hotsets de tenir en mémoire et aux disques d'être moins souvent touchés. Horizontalement, je décharge le système primaire avec des réplicas de lecture, j'y dirige les lectures et je garde la charge d'écriture focalisée, ce qui réduit les blocages. Pour cela, l'application a besoin d'un splitting lecture/écriture et d'une stratégie en cas de retards, afin que les lecteurs ne consultent pas des données obsolètes. En cas de forte fluctuation du trafic, je calcule l'auto-scaling du côté de l'application pour éviter que des centaines d'utilisateurs PHP ne transforment soudainement le pool de bases de données en une seule. Saturation de l'entreprise.
Préciser le modèle de charge : Rendre la pression sur le pool prévisible
Je quantifie la pression à l'aide d'une simple règle du pouce : travailleurs Web simultanés × temps de maintien moyen des requêtes ≈ requis Connections. Si le temps de maintien moyen passe de 50 ms à 200 ms à cause des E/S ou des verrous, le besoin est quadruplé. Exemple : 120 travailleurs PHP et 0,2 s de temps moyen de base de données impliquent 24 connexions occupées simultanément avec une répartition idéale - dans des conditions réelles avec des bursts et des longs tails, je prévois au moins 2 à 3 fois plus. Je mets en outre des réserves de côté pour les charges de travail Admin/Cron et je sépare les tâches critiques dans des pools distincts. J'évite ainsi que des pages courtes ne meurent de faim derrière des transactions peu nombreuses mais longues.
Dimensionner le serveur web et les workers PHP en fonction de la limite de la base de données
Je règle le nombre de PHP-FPM-workers sur le MySQL-plutôt que de les choisir de manière isolée „plus grand = meilleur“. Si max_connections est de 600, je donne par exemple 400 slots backend durs au pooling/proxy et je limite PHP-FPM à un nombre qui ne dépasse pas durablement ces slots, même aux heures de pointe. Le contrôle des admissions empêche les avalanches : Les files d'attente NGINX ou App doivent avoir des limites supérieures et, en cas de débordement, je livre délibérément 429/503 avec retry after au lieu de files d'attente illimitées. Pour PHP-FPM, j'évite les pm.max_children trop agressifs et j'établis des timeouts d'E/S courts afin que les backends suspendus ne lient pas des lots de travail entiers. Je combine les processus ondemand ou dynamiques avec des limites de taux pour les bots, afin que la mise à l'échelle ne „gonfle“ pas le pool de bases de données.
; php-fpm.conf (exemple)
pm = dynamic
pm.max_children = 160
pm.start_servers = 20
pm.min_spare_servers = 20
pm.max_spare_servers = 40
request_terminate_timeout = 30s
Transactions, isolation et verrouillage sous contrôle
Les longues transactions sont un poison pour les Saturation, Je ne veux pas que les transactions soient trop longues, car elles maintiennent les verrous, font croître Undo et ralentissent les autres requêtes. Je maintiens les transactions aussi courtes que possible : d'abord lire les données, puis les écrire rapidement, les commiter immédiatement. Je vérifie si REPEATABLE READ est vraiment nécessaire ou si READ COMMITTED est suffisant, ce qui permet de réduire les verrous Next-Key/Gap. J'utilise SELECT ... FOR UPDATE de manière ciblée et je limite le nombre de lignes concernées avec des indices appropriés. Je laisse l'autocommit actif pour les accès en lecture seule et je fractionne les écritures en petites unités fermées. J'évalue régulièrement les deadlocks et j'interromps les sessions en attente depuis longtemps au lieu de les laisser en „Waiting for lock“ pendant des minutes - cela réduit sensiblement le Threads_running.
Peaufinage d'InnoDB pour des temps de latence constants
Je règle le chemin des logs et des E/S de manière à ce que les latences de commit restent stables sous la charge. Des redo-logs plus grands (innodb_log_file_size) lissent les pics, le flushing adaptatif (innodb_adaptive_flushing) empêche les bégaiements, et des innodb_io_capacity(-max) réalistes correspondent aux performances réelles du stockage. Le buffer pool reste suffisamment grand pour le hotset, tandis que je choisis délibérément innodb_flush_log_at_trx_commit en fonction des exigences de cohérence. Les clés primaires sont monotones (par ex. AUTO_INCREMENT) afin de minimiser les splits de page et les E/S aléatoires. Important : je mesure les latences p95/p99 avant/après chaque modification et j'observe les taux de fsync et de redo-flush - c'est la seule façon de savoir si l'optimisation a un réel effet ou si elle ne fait que déplacer la pression.
[mysqld]
innodb_log_file_size = 2G
innodb_flush_method = O_DIRECT
innodb_io_capacity = 1000
innodb_io_capacity_max = 2000
innodb_adaptive_flushing = 1
Ne pas oublier les paramètres du système d'exploitation et du réseau
La saturation se manifeste également dans les files d'attente du noyau et les descripteurs de fichiers. J'augmente les files d'attente d'acceptation et la plage de ports libres pour que les pics de courte durée ne se heurtent pas aux limites du système d'exploitation. Je fixe des intervalles de keepalive modérés et je vérifie open_files_limit ainsi que fs.file-max, afin que de nombreuses connexions simultanées ne se heurtent pas à la limite des fichiers. Côté MySQL, un back_log de taille appropriée permet de mettre en mémoire tampon les bursts de connexion entrants jusqu'à ce que le planificateur de threads s'en charge. Ces vis de réglage n'atténuent pas la cause, mais permettent de gagner de précieuses millisecondes pendant lesquelles le pool traite au lieu de rejeter.
# sysctl (exemples)
net.core.somaxconn = 1024
net.ipv4.ip_local_port_range = 10240 65535
fs.file-max = 200000
# my.cnf (complément)
back_log = 512
open_files_limit = 100000
Observabilité : rendre la saturation visible
Je construis des tableaux de bord autour de quelques métriques significatives : Threads_running vs. Threads_connected, Max_used_connections par rapport à max_connections, latences de requête p95/p99, innodb_row_lock_time, compteur Handler* et Connection-Errors. Je fais régulièrement tourner le journal des requêtes lentes et fixe des seuils pragmatiques (p. ex. 200-300 ms) afin que les déclarations „moyennement coûteuses“ qui, au total, encombrent le pool, restent visibles. J'utilise le schéma de performance et les vues sys pour identifier les déclarations à chaud, les attentes et les meilleurs consommateurs. Je place délibérément les alarmes en dessous de la limite dure (70-80% de la limite), de sorte que je puisse intervenir avant les pannes réelles.
Tests de charge, backpressure et dégradation
Je teste la charge de manière réaliste avec des rampes d'accélération, des pics courts et des phases d'accélération plus longues. L'objectif est d'obtenir des temps de réponse p95 stables et un débit contrôlé - pas seulement des requêtes/s maximales. En cas de surcharge, Backpressure intervient : limites de file d'attente, délais d'attente échelonnés et retours exponentiels au lieu de l'obstination. Je dégrade les fonctionnalités de manière ciblée, avant que les DB tombe : masquer les widgets coûteux, répondre aux agrégations avec des données „stale“, ralentir temporairement les fonctions Write-Heavy. Un plan d'urgence clair avec runbook (vérifier les logs, agrandir le pool, vider/réchauffer les caches, mettre en pause les jobs d'arrière-plan) permet d'économiser, dans les phases chaudes, des minutes qui seraient autrement perdues dans un débogage aveugle.
Read-Replicas dans la pratique : équilibrer la latence et la cohérence
Les réplicas de lecture dissocient la lecture et l'écriture, mais entraînent un retard de réplication. Je route les lectures non critiques sur les réplicas et garde délibérément le chemin „read-after-write“ sur la primaire ou j'utilise une courte „stickiness“ après les écritures. Je mesure en permanence le retard de réplication et, en cas de retard trop important, je replace automatiquement les lectures sur la primaire. Je déplace les rapports ou les index de recherche planifiés de manière ciblée sur les répliques et je les réduis sous la charge de pointe afin que la primaire puisse maintenir sa latence pour les utilisateurs. Important : ne jamais autoriser les accès en écriture sur les répliques - les chemins mixtes aboutissent sinon à des incohérences difficiles à trouver.
WordPress sous haute charge : recettes pratiques
Outre le cache des pages/objets, une cure pour wp_options vaut la peine : ne mettre le drapeau autoload que pour les options vraiment globales et petites et désencombrer le reste. Pour WooCommerce, je vérifie les index pour wp_postmeta (combinaison de post_id et meta_key) et j'évite les requêtes qui font défiler des tableaux entiers avec des préfixes LIKE. Je découple WP-Cron sur System-Cron et j'effectue les tâches lourdes en heures creuses. Les points de terminaison REST et AJAX ont leurs propres limites de taux et des délais d'attente courts, afin qu'ils ne bloquent pas le même pool que le rendu de la page. Pour les vues de liste, je remplace les tris coûteux sur meta_value par des champs prétraités ou des colonnes calculées - cela réduit les scans complets et maintient la qualité des données. Fils de discussion libre.
# System-Cron au lieu de WP-Cron
*/5 * * * /usr/bin/wp cron event run --due-now --path=/var/www/html >/dev/null 2>&1
Résumé pour une action rapide
J'aborde la saturation des connexions à la base de données de manière systématique : Limiter les causes, augmenter la configuration de manière dosée et réduire les temps de requête pour que Connections se libèrent. Ensuite, je stabilise avec le pooling et la mise en cache, car ces leviers retirent la majeure partie de la demande directement de la base de données. La mise à l'échelle n'intervient que lorsque les métriques prouvent que le réglage est épuisé et que l'application peut gérer proprement plusieurs nœuds. Le monitoring avec des alertes claires sur la charge de 70-80% me protège des surprises et me donne le temps d'ajuster les limites ou les stratégies de cache. Si je maintiens cet ordre, MySQL reste résilient en cas de charge élevée, le nombre d'erreurs diminue et les sites fournissent rapidement des données, même en période de pointe. stable.


