MySQL vs MariaDB : WordPress 504 — qui s’effondre en premier lors d’un pic de trafic

Cet article vous a aidé ?

Les 504 sur WordPress ne sont que rarement « un problème WordPress ». C’est un symptôme : la requête est entrée dans votre pile et n’a pas pu en sortir avant que la minuterie de la passerelle n’atteigne zéro. La plupart du temps, c’est la base de données qui est allée mourir — calmement, poliment, et avec juste assez de logs pour vous gâcher l’après-midi.

Si vous hésitez entre MySQL et MariaDB pour WordPress sous trafic en creux, voici la vérité franche : l’un comme l’autre peut survivre, et l’un comme l’autre peut s’effondrer. La base qui « s’effondre en premier » est généralement celle que vous avez configurée comme si c’était 2014, déployée comme un animal de compagnie et supervisée comme une rumeur. Parlons des modes de panne, pas de fidélité à une marque.

Ce qu’un 504 WordPress signifie réellement en production

Un 504 n’est pas « WordPress qui a expiré ». C’est votre passerelle (souvent Nginx, parfois un ALB, parfois un CDN) qui dit : « J’ai demandé une réponse à un upstream, et il n’a pas répondu assez vite. » L’upstream peut être PHP-FPM, qui peut être bloqué sur MySQL, qui peut être bloqué sur le disque, qui peut être bloqué sur un mutex, qui peut être bloqué par vos choix de vie douteux.

Sur une pile WordPress typique :

  • Client atteint le CDN ou le load balancer.
  • La requête arrive à Nginx/Apache.
  • Le chemin dynamique va vers PHP-FPM.
  • PHP exécute le travail applicatif et appelle MySQL/MariaDB.
  • La base lit depuis le buffer pool ou le disque, verrouille des lignes, écrit redo/undo et renvoie les résultats.

Sous un pic, le maillon le plus faible n’est pas nécessairement le composant le plus lent ; c’est celui qui abandonne la charge le moins gracieusement. Une base saturée ne tombe généralement pas rapidement. Elle file la file d’attente. La mise en file ressemble à « ça marche encore, juste plus lent », ce qui devient « tout expire », puis « le canal d’incident est devenu une séance de thérapie de groupe. »

Il y a deux façons principales pour une base de transformer un pic en 504 :

  1. Pression sur les connexions : trop de workers PHP simultanés qui essaient de se connecter ou d’exécuter des requêtes, provoquant une explosion de threads, des changements de contexte, ou une famine de ressources.
  2. Inflation de latence : les requêtes ralentissent à cause du IO disque, de la contention des verrous, des misses du buffer pool, du purge lag, de la pression sur les redo logs, ou tout simplement de mauvais plans de requête.

Ainsi « qui s’effondre en premier » revient à : quel moteur + configuration vous donne une meilleure latence tail et un comportement plus prévisible quand la file commence à se former.

MySQL vs MariaDB lors d’un pic de trafic : qui échoue en premier (et pourquoi)

Réalité de base : WordPress n’est pas un benchmark de base de données

WordPress n’est pas de l’OLTP pur. C’est beaucoup de petites lectures, quelques écritures, et un tour de magie nommé wp_options qui peut devenir la table la plus chaude du bâtiment. Il inclut aussi du SQL de plugins… d’une moralité variable.

Ce qui importe lors des pics :

  • Gestion des connexions : modèle thread-per-connection vs comportement de pooling/boucles de threads.
  • Comportement d’InnoDB sous contention : verrous de ligne, verrous métadonnées, purge, flushing.
  • Prévisibilité de l’optimiseur : plans stables, bonnes décisions d’indexation.
  • Observabilité et outils : pouvez-vous voir ce qui se passe avant que les 504 ne deviennent un incendie ?

MySQL : prévisible si vous le maintenez banal, dangereux si vous laissez les « defaults »

MySQL moderne (8.x) est solide pour WordPress. Il est aussi sans concession dans sa complexité. Les valeurs par défaut sont conçues pour démarrer partout, pas pour survivre au jour où votre page d’accueil se retrouve en une.

Où MySQL a tendance à bien se comporter sous les pics :

  • InnoDB stable avec instrumentation mature (Performance Schema, sys schema).
  • Améliorations de l’optimiseur au fil des ans qui aident souvent sur des charges mixtes.
  • Outils et écosystème de réplication matures dans de nombreuses organisations.

Où MySQL s’effondre souvent en premier (dans de vraies piles WordPress) :

  • Tempêtes de connexions quand PHP-FPM augmente les workers et que chacun ouvre une connexion DB ; vous devenez CPU-bound sur les threads et mutex bien avant d’être « out of CPU » au sens habituel.
  • Arrêts liés au IO quand le buffer pool est sous-dimensionné et que les redo log/flushing sont réglés comme sur un portable.
  • Accumulations de verrous métadonnées causées par des DDL ou de longues transactions (souvent des tâches d’admin « inoffensives » pendant les pics).

MariaDB : parfois plus indulgent sur la concurrence, parfois un piège de compatibilité

MariaDB est née d’un fork avec une façade familière. Avec le temps elle est devenue sa propre base avec sa propre personnalité. Pour WordPress, la différenciation la plus pratique que vous remarquerez sous les pics concerne le comportement en concurrence (surtout si vous utilisez le thread pool de MariaDB) et la familiarité opérationnelle selon ce que votre équipe a déjà exploité.

Où MariaDB a tendance à bien faire face aux pics :

  • Thread pool (dans beaucoup de builds MariaDB) peut réduire le thrash de threads sous un grand nombre de connexions en limitant les workers actifs et en ordonnançant.
  • Réglages opérationnels que certaines équipes trouvent parfois plus simples selon le packaging distro et les valeurs par défaut.

Où MariaDB s’effondre en premier (encore une fois, sur le terrain) :

  • Hypothèses « drop-in MySQL » qui se cassent sur des comportements SQL limites, des variables système ou des attentes d’outillage — surtout si vous mixez des composants d’écosystème qui supposent les sémantiques de MySQL 8.
  • Surprises de plan de requête si vous étiez habitué au comportement de l’optimiseur MySQL et ne validez pas avec EXPLAIN après des mises à jour.
  • Incompatibilités de réplication/GTID dans des environnements mixtes lors de migrations, ce qui transforme « ajouter une réplica » en « pourquoi la réplica est-elle fâchée ? »

Si vous voulez la réponse directe : lors d’un pic soudain WordPress, la première chose à s’effondrer est généralement la gestion des connexions, pas « MySQL vs MariaDB ». MariaDB avec thread pool peut rester debout plus longtemps lors d’une tempête de connexions. MySQL peut très bien faire aussi, mais il a souvent besoin que vous soyez délibéré : limiter les workers PHP-FPM, utiliser du pooling (ProxySQL ou équivalent), et arrêter de prétendre que max_connections est une fonctionnalité de performance.

Blague #1 : Un pic de trafic est la façon dont la nature vous demande si votre « ça marche en staging » paie aussi le loyer.

Faits et historique qui comptent encore pour votre panne

  • Fait 1 : MariaDB a été forkée de MySQL après qu’Oracle ait acquis Sun Microsystems (et donc MySQL) en 2010, conduite par des inquiétudes sur la gouvernance future de MySQL.
  • Fait 2 : MySQL 8.0 a introduit des changements majeurs — refonte du dictionnaire de données, amélioration du performance schema, et de nombreuses améliorations de l’optimiseur — rendant le modèle mental « MySQL 5.7 » obsolète.
  • Fait 3 : Les numéros de version de MariaDB ont délibérément divergé (10.x) et ne sont pas directement comparables à MySQL 8.0 ; les traiter comme « plus grand = plus récent » mène à de mauvaises attentes.
  • Fait 4 : Dans beaucoup de distributions, les paquets « mysql » sont devenus des méta-paquets pointant vers MySQL ou MariaDB ; des équipes ont découvert qu’elles tournaient sur MariaDB par accident. C’est amusant une seule fois.
  • Fait 5 : WordPress s’est historiquement appuyé sur la compatibilité MySQL, mais WordPress moderne ne vous protège pas du SQL mauvais d’un plugin ; le moteur de base ne vous sauvera pas d’un plugin qui fait des scans de table à chaque requête.
  • Fait 6 : InnoDB est devenu le moteur de stockage par défaut il y a des années, et presque toutes les charges WordPress sérieuses devraient être InnoDB ; MyISAM en production est une capsule temporelle aux bords tranchants.
  • Fait 7 : Le query cache (ancienne fonctionnalité MySQL) a été supprimé dans MySQL 8 et est désactivé/irrrelevant dans la plupart des setups modernes ; si quelqu’un suggère de l’activer, il cite le folklore.
  • Fait 8 : « Plus de réplicas » ne résout pas la contention d’écritures ; WordPress a un pattern de hotspot d’écritures (sessions, options, mises à jour de transients) qui peut bottleneck le primaire même si les lectures sont déchargées.
  • Fait 9 : Les modèles thread-per-connection peuvent devenir liés à la planification CPU bien avant que l’utilisation CPU n’apparaisse « élevée » ; le graphe ment parce que le temps est passé en context switching et en attente.

Mode d’emploi pour diagnostic rapide : trouver le goulot en minutes

Ceci est l’ordre qui gagne les incidents. Pas parce que c’est théoriquement pur, mais parce que ça réduit rapidement le champ de recherche.

Premier : prouver où le timeout se produit

  1. Logs de la passerelle (Nginx/ALB/CDN) : est-ce un timeout upstream, ou côté client ?
  2. Statut PHP-FPM : les workers sont-ils saturés, slowlog déclenché, ou bloqués sur la DB ?
  3. DB : connexions et requêtes actives : les threads explosent-ils, ou quelques requêtes sont-elles coincées ?

Second : décider « tempête de connexions » vs « requêtes lentes » vs « verrous »

  • Tempête de connexions : beaucoup de threads en sleeping/connecting, Threads_connected élevé, hausse du temps CPU sys, beaucoup de courtes requêtes expirant, PHP-FPM saturé.
  • Requêtes lentes / IO : misses du buffer pool, latence élevée des lectures disque, fortes lectures InnoDB, temps de requête longs sans verrous évidents.
  • Verrous : « Waiting for table metadata lock », « Waiting for record lock », beaucoup de requêtes bloquées derrière un écrivain, timeouts d’attente de verrou.

Troisième : arrêter l’hémorragie en sécurité

  • Réduire la charge en périphérie : activer le cache, limiter les endpoints abusifs, désactiver temporairement les routes coûteuses (search, wp-cron via le web, XML-RPC si applicable).
  • Limiter la concurrence : réduire les enfants PHP-FPM si la DB se noie ; laisser des workers PHP infinis frapper la DB n’est pas du « scaling ».
  • Tuer les pires fautifs : terminer la requête qui maintient des verrous ou qui tourne depuis des minutes, mais confirmez que ce n’est pas un DDL critique ou une sauvegarde.

Idée paraphrasée (John Allspaw) : La fiabilité est une propriété de l’ensemble du système, pas d’un seul composant.

Tâches pratiques : commandes, sorties et décisions (12+)

Tout ce qui suit est conçu pour être exécutable sur un hôte Linux typique avec WordPress + Nginx + PHP-FPM + MySQL/MariaDB. Adaptez les chemins et noms de services selon votre distro.

Task 1: Confirm the 504 is an upstream timeout (not DNS, not client)

cr0x@server:~$ sudo tail -n 20 /var/log/nginx/error.log
2025/12/29 10:11:45 [error] 1187#1187: *28491 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 203.0.113.10, server: example.com, request: "GET / HTTP/1.1", upstream: "fastcgi://unix:/run/php/php8.2-fpm.sock", host: "example.com"

Ce que cela signifie : Nginx a attendu PHP-FPM et n’a pas reçu d’en-têtes à temps. La DB peut toujours être la cause, mais le goulot immédiat est la latence upstream.

Décision : Allez vérifier la saturation de PHP-FPM et le traçage des requêtes lentes ensuite.

Task 2: Check PHP-FPM pool saturation quickly

cr0x@server:~$ sudo ss -s
Total: 1560 (kernel 0)
TCP:   932 (estab 210, closed 590, orphaned 0, timewait 560)

Transport Total     IP        IPv6
RAW       0         0         0
UDP       12        10        2
TCP       342       290       52
INET      354       300       54
FRAG      0         0         0

Ce que cela signifie : Beaucoup de timewait et beaucoup de churn TCP peuvent indiquer des tempêtes de connexions (soit PHP↔DB soit client↔web), mais ce n’est pas une preuve.

Décision : Inspectez le statut PHP-FPM et le slowlog, puis les threads DB.

Task 3: Check PHP-FPM process pressure

cr0x@server:~$ ps -o pid,etime,pcpu,pmem,cmd -C php-fpm8.2 --sort=-pcpu | head
  PID     ELAPSED %CPU %MEM CMD
 2261    01:12:08 18.3  1.6 php-fpm: pool www
 2377    00:45:12 16.9  1.5 php-fpm: pool www
 2410    00:21:33 15.2  1.5 php-fpm: pool www
 2011    02:10:40  0.3  0.6 php-fpm: master process (/etc/php/8.2/fpm/php-fpm.conf)

Ce que cela signifie : Les workers sont occupés depuis longtemps. Cela veut souvent dire qu’ils sont bloqués — DB, système de fichiers, appels HTTP externes, ou PHP consommant du CPU.

Décision : Activez/vérifiez le slowlog ou la page de statut PHP-FPM pour identifier où le temps est passé.

Task 4: Find “server reached pm.max_children” in PHP-FPM logs

cr0x@server:~$ sudo grep -R "max_children" -n /var/log/php8.2-fpm.log | tail -n 5
[29-Dec-2025 10:11:44] WARNING: [pool www] server reached pm.max_children setting (40), consider raising it

Ce que cela signifie : Les requêtes sont mises en file à PHP-FPM. Augmenter max_children peut aider, ou peut simplement augmenter la pression sur la DB et empirer la panne.

Décision : Si la DB est déjà saturée, n’augmentez pas aveuglément. Confirmez d’abord la marge de manœuvre de la DB.

Task 5: Check database identity and version (don’t guess)

cr0x@server:~$ mysql -NBe "SELECT VERSION(), @@version_comment;"
10.6.18-MariaDB-0ubuntu0.22.04.1	Ubuntu 22.04

Ce que cela signifie : Vous êtes sur MariaDB 10.6, pas Oracle MySQL. Cela impacte les variables disponibles, le comportement du performance schema et les hypothèses d’outillage.

Décision : Utilisez des réglages/metrics adaptés au moteur. Ne collez pas des conseils MySQL 8 uniquement sur MariaDB.

Task 6: Check connection count and thread state distribution

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Threads_%';"
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_cached    | 18    |
| Threads_connected | 420   |
| Threads_created   | 9812  |
| Threads_running   | 64    |
+-------------------+-------+

Ce que cela signifie : 420 connexions et 64 threads en exécution. Threads_created est élevé, ce qui suggère du churn ou un thread cache insuffisant (ou des tempêtes de connexions). Sous les pics, cela peut devenir un combat d’ordonnancement.

Décision : Si les connexions évoluent linéairement avec le trafic, priorisez le pooling/la limitation des connexions (ProxySQL, connexions persistantes avec précaution, ou moins de workers PHP).

Task 7: Identify the top running queries right now

cr0x@server:~$ mysql -e "SHOW FULL PROCESSLIST;" | head -n 15
Id	User	Host	db	Command	Time	State	Info
1283	wp	wpapp:42110	wpdb	Query	12	Sending data	SELECT option_name, option_value FROM wp_options WHERE autoload = 'yes'
1290	wp	wpapp:42118	wpdb	Query	11	Waiting for table metadata lock	ALTER TABLE wp_posts ADD INDEX idx_post_date (post_date)
1299	wp	wpapp:42130	wpdb	Query	10	Locked	UPDATE wp_options SET option_value='...' WHERE option_name='_transient_timeout_x'

Ce que cela signifie : Vous avez un ALTER TABLE en attente d’un verrou métadonnées et un UPDATE qui est verrouillé. Pendant ce temps, beaucoup de requêtes frappent wp_options.

Décision : Arrêtez le DDL pendant le pic (tuez-le si c’est sûr), puis traitez les patterns d’accès à wp_options et le bloat d’autoload.

Task 8: Confirm metadata lock contention (MySQL and MariaDB)

cr0x@server:~$ mysql -e "SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS FROM performance_schema.metadata_locks WHERE LOCK_STATUS='PENDING' LIMIT 10;"
+--------------+-------------+-----------+-------------+
| OBJECT_SCHEMA| OBJECT_NAME | LOCK_TYPE | LOCK_STATUS |
+--------------+-------------+-----------+-------------+
| wpdb         | wp_posts    | EXCLUSIVE | PENDING     |
+--------------+-------------+-----------+-------------+

Ce que cela signifie : Quelque chose veut un verrou métadonnées exclusif (typiquement un DDL) et attend, bloquant souvent ou étant bloqué par des requêtes/transactions longues.

Décision : Trouvez le bloqueur (transaction longue) et attendez-le ou tuez-le. Déplacez le DDL en dehors des pics et utilisez des outils de changement de schéma en ligne si nécessaire.

Task 9: Measure InnoDB buffer pool pressure

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
+---------------------------------------+-----------+
| Variable_name                         | Value     |
+---------------------------------------+-----------+
| Innodb_buffer_pool_read_requests      | 986543210 |
| Innodb_buffer_pool_reads              | 5432109   |
+---------------------------------------+-----------+

Ce que cela signifie : Innodb_buffer_pool_reads sont des lectures depuis le disque. Si cela monte rapidement pendant les pics, vous manquez de cache et payez la latence IO par requête.

Décision : Augmentez le buffer pool (dans les limites de la RAM), réduisez le working set (nettoyage autoload, index), ou ajoutez des couches de cache pour réduire les lectures DB.

Task 10: Check redo log / checkpoint pressure signals

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_os_log_written'; SHOW GLOBAL STATUS LIKE 'Innodb_log_waits';"
+------------------------+------------+
| Variable_name          | Value      |
+------------------------+------------+
| Innodb_os_log_written  | 8123456789 |
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| Innodb_log_waits | 421   |
+------------------+-------+

Ce que cela signifie : Des Innodb_log_waits non nuls et en augmentation indiquent des sessions en attente d’espace dans le redo log / du flushing. Sous les pics, cela peut plomber la latence d’écriture et se propager en timeouts.

Décision : Reconsidérez la taille des redo logs et la capacité IO ; réduisez l’amplification d’écriture (comportement des plugins, churn de transients), et assurez-vous que la latence de stockage est raisonnable.

Task 11: Identify lock waits (transactions blocking others)

cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | sed -n '1,120p'
TRANSACTIONS
------------
Trx id counter 4892011
Purge done for trx's n:o < 4891900 undo n:o < 0 state: running
History list length 3212
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 4892007, ACTIVE 19 sec
2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1
MySQL thread id 1299, OS thread handle 140312351889152, query id 712381 wpapp 10.0.0.12 wpdb updating
UPDATE wp_options SET option_value='...' WHERE option_name='_transient_timeout_x'

Ce que cela signifie : Une transaction est active depuis 19 secondes et met à jour wp_options. La longueur de la history list suggère un purge lag ; les transactions longues peuvent bloquer la purge et augmenter la pression undo.

Décision : Trouvez ce qui tient des transactions longues (jobs batch, sauvegardes, écrans admin), corrigez-les, et envisagez de réduire la portée des transactions dans l’app (difficile avec WordPress, mais les plugins peuvent être corrigés ou supprimés).

Task 12: Check for autoloaded options bloat (WordPress hotspot)

cr0x@server:~$ mysql -NBe "SELECT COUNT(*) AS autoload_rows, ROUND(SUM(LENGTH(option_value))/1024/1024,2) AS autoload_mb FROM wp_options WHERE autoload='yes';"
4123	18.74

Ce que cela signifie : 18.74 MB d’options autoloadées chargées fréquemment. Ce n’est pas « un petit overhead ». C’est une taxe sur chaque requête non mise en cache.

Décision : Réduisez l’ensemble autoload (corrigez les plugins, déplacez les gros blobs, mettez autoload=no quand c’est sûr). Cela peut être plus impactant que de remplacer MySQL par MariaDB.

Task 13: Confirm storage latency (because databases have feelings)

cr0x@server:~$ iostat -xz 1 3
Linux 6.5.0 (server) 	12/29/2025 	_x86_64_	(8 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          18.22    0.00    9.31   14.70    0.00   57.77

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz  aqu-sz  %util
nvme0n1         180.0   8200.0     0.0    0.0    6.20    45.6     95.0   9100.0     5.0    5.0   18.40    95.8    2.10  92.0

Ce que cela signifie : w_await est 18ms et l’utilisation du device est 92%. Pendant les pics, c’est suffisant pour gonfler la latence des requêtes et créer des files d’attente.

Décision : Soit réduisez la charge d’écriture (cache, tuning du flushing, corriger les plugins) soit améliorez le stockage / isolez le volume DB / assurez une config RAID/NVMe adéquate.

Task 14: Enable slow query logging temporarily (surgical, not forever)

cr0x@server:~$ mysql -e "SET GLOBAL slow_query_log=ON; SET GLOBAL long_query_time=0.5; SET GLOBAL log_queries_not_using_indexes=ON;"

Ce que cela signifie : Vous capturez les requêtes plus lentes que 0.5s et celles qui n’utilisent pas d’index. Cela peut être bruyant ; ne laissez pas actif en permanence sous fort volume.

Décision : Utilisez-le pendant la fenêtre d’incident, analysez les principaux coupables puis désactivez-le ou réglez-le à nouveau.

Task 15: Observe top tables by read/write pressure (InnoDB metrics)

cr0x@server:~$ mysql -e "SELECT * FROM sys.schema_table_statistics ORDER BY rows_read DESC LIMIT 5;"
+----------------+------------+-----------+----------------+----------------+------------+------------+------------+-------------------+-------------------+
| table_schema   | table_name | total_rows| rows_fetched   | fetch_latency  | rows_insert| rows_update| rows_delete| io_read_requests  | io_write_requests |
+----------------+------------+-----------+----------------+----------------+------------+------------+------------+-------------------+-------------------+
| wpdb           | wp_options | 52341     | 98122312       | 00:14:12.123456| 120        | 53210      | 8          | 842113            | 290112            |
| wpdb           | wp_postmeta| 984122    | 55122311       | 00:12:40.654321| 210        | 1201       | 12         | 721223            | 120991            |
+----------------+------------+-----------+----------------+----------------+------------+------------+------------+-------------------+-------------------+

Ce que cela signifie : Les suspects habituels. Si wp_options et wp_postmeta dominent, corrigez les patterns WordPress avant d’acheter plus de hardware.

Décision : Priorisez l’indexation, l’hygiène autoload, le caching d’objet et l’audit des plugins.

Les schémas habituels d’effondrement (et à quoi ils ressemblent)

Pattern A: Connection storm and thread thrash

Celui-ci est courant quand un post devient viral ou qu’un botnet commence à « crawler » vos endpoints dynamiques. PHP-FPM augmente les workers. Chaque worker ouvre une connexion DB (ou plusieurs). La DB lance des threads. Le temps CPU est consommé en context switching, contention de mutex et gestion d’un trop grand nombre de sessions.

Ce que vous voyez :

  • Threads_connected monte en flèche.
  • Threads_created augmente rapidement.
  • Le temps système CPU augmente, la charge moyenne grimpe, mais « le travail utile » ne suit pas.
  • Beaucoup de requêtes sont courtes, mais tout attend en file.

Qui s’effondre en premier ? Les deux peuvent. Mais MariaDB avec thread pool peut être plus gracieuse ici si configurée, car elle limite l’exécution concurrente et réduit le thrash. MySQL peut l’égaler si vous plafonnez la concurrence côté application et/ou ajoutez un proxy pool.

Pattern B: wp_options hotspot + autoload bloat

Les options autoloadées de WordPress sont chargées fréquemment. Ajoutez quelques plugins qui stockent de grands tableaux sérialisés avec autoload=yes, et vous vous construisez un petit déni de service auto-infligé.

Ce que vous voyez :

  • SELECT option_name, option_value FROM wp_options WHERE autoload='yes' apparaît constamment.
  • Churn du buffer pool (si le working set ne tient pas en RAM).
  • Le CPU monte avec le parsing des requêtes et le traitement des lignes.

Qui s’effondre en premier ? Celui qui a moins de RAM, de pires indexes ou une moins bonne stratégie de cache. Le choix du moteur ne vous sauvera pas d’un abus d’autoload.

Pattern C: Lock pileups from writes and background tasks

Les pics de trafic coïncident souvent avec plus de commentaires, connexions, mises à jour de panier (si WooCommerce) et mises à jour de transients. Les écritures créent des verrous. Des requêtes longues ou des tâches admin « inoffensives » peuvent tenir des verrous et bloquer le monde.

Ce que vous voyez :

  • États dans le processlist : Locked, Waiting for…
  • Le status InnoDB montre des transactions longues et une longueur croissante de la history list.
  • Timeouts d’attente de verrou et deadlocks.

Qui s’effondre en premier ? Encore une fois, les deux. La différence tient à l’opérationnel : à quelle vitesse vous voyez le bloqueur et comment vous pouvez atténuer en sécurité sans aggraver la situation.

Pattern D: IO saturation (the quiet killer)

Quand la latence disque grimpe, tout ralentit. La base ne « crashe » pas. Elle devient un amplificateur de latence. Les workers PHP s’empilent. Nginx timeoute. Vous obtenez des 504 et beaucoup d’opinions confuses.

Ce que vous voyez :

  • iowait augmente.
  • Utilisation du disque %util haute, await qui grandit.
  • Innodb_buffer_pool_reads monte rapidement.
  • Checkpoints / log waits.

Blague #2 : La base de données n’est pas « tombée ». Elle a juste pris une longue et profonde pause pour réfléchir à vos choix de stockage.

Trois mini-récits d’entreprise tirés du terrain

Mini-récit 1 : L’incident causé par une mauvaise hypothèse

L’entreprise était en migration de MySQL plus ancien vers MariaDB parce que « c’est drop-in ». Ils ont fait les parties sensées : répliquer les données, répéter la coupure, valider les tests applicatifs. Ils ont fait la partie moins sensée : supposer que leurs outils opérationnels se comporteraient de la même façon.

Le jour du lancement, le trafic a piqué — le marketing a fait son travail. WordPress a commencé à renvoyer des 504. L’on-call a suivi le playbook normal : sortir les top queries du dashboard de monitoring, vérifier le décalage de réplication et inspecter les compteurs MySQL habituels.

Le tableau semblait étrangement calme. Les connexions étaient « ok ». Le temps de requête « ok ». Pourtant Nginx time-outait et PHP-FPM criait « max_children ». L’équipe a cherché le problème côté web pendant une demi-heure parce que les graphes de la base omettaient des métriques.

La cause réelle était simple et embarrassante : leur exporter interrogait des tables Performance Schema spécifiques à MySQL et retournait des métriques partielles sur MariaDB. La DB était saturée de lock waits et de latence disque, mais les graphes omettaient l’information. Ils ont corrigé en déployant un exporter compatible MariaDB et en ajoutant des panneaux « SHOW PROCESSLIST sample ». Les 504 étaient bien un problème de base ; l’incident était un problème d’observabilité.

Mini-récit 2 : L’optimisation qui a échoué

Une autre org avait un site WordPress périodiquement assailli par des crawlers. Quelqu’un a décidé d’augmenter la concurrence partout : augmenter PHP-FPM max_children, augmenter MySQL max_connections, et monter le nombre de workers web. L’intention était noble : « gérer plus de trafic ». L’effet fut plutôt « gérer plus de souffrance ».

Le pic suivant a frappé et la DB est tombée plus durement qu’avant. Pas un crash — pire. Elle est restée en ligne mais répondait lentement. Le CPU n’était pas à 100 %, mais la latence est devenue verticale. Threads_created a explosé. Le kernel passait son temps en context switching. La queue de stockage est restée haute parce que les écritures arrivaient plus vite qu’elles ne pouvaient être flushées. Nginx renvoyait des 504 comme si c’était son travail.

L’équipe a rollbacké les changements de concurrence et le système s’est stabilisé. Plus tard ils ont mis en place la correction peu glamour : limiter PHP-FPM à un nombre que la DB peut soutenir, ajouter un pooler de connexions, et mettre du caching devant les endpoints les plus lourds. La leçon : augmenter les limites augmente la taille du rayon d’impact. Cela n’augmente pas la capacité sauf si autre chose change.

Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Cette équipe faisait tourner WordPress avec un calendrier de changements strict et une habitude que tout le monde moquait : ils répétaient la réponse aux incidents et gardaient une politique de « peak freeze ». Pendant des événements de trafic prévus (lancements produits, mentions média importantes), ils ne déployaient pas de changements de schéma, mises à jour de plugins ou « tweaks rapides » dans wp-admin.

Un après-midi, le trafic a triplé de façon inattendue à cause d’un buzz social. Le site a ralenti, mais n’a pas implosé. Leur cache Nginx servait la plupart du trafic anonyme. PHP-FPM avait un cap strict. La DB avait de la marge car le working set tenait dans le buffer pool et le slow query log avait été échantillonné et nettoyé des semaines plus tôt.

Ils ont tout de même reçu des alertes : le lag de réplication a un peu grossi, la latence p95 des requêtes a grimpé. Mais rien n’a atteint les seuils de timeout. Leur canal d’incident est resté ennuyeux, ce qui est le plus grand compliment que l’on puisse faire à un système SRE.

La mesure qui a sauvé n’était pas un moteur magique. C’était la discipline opérationnelle : planification de capacité, caching, hygiène des requêtes, et refuser de faire des DDL au pire moment.

Erreurs courantes : symptôme → cause racine → correction

  • Symptôme : pics de 504, les logs PHP-FPM montrent max_children atteint, CPU DB semble « pas si élevé ».
    Cause racine : tempête de connexions et overhead d’ordonnancement des threads ; inflation de latence sans CPU à fond.
    Correction : Limiter PHP-FPM, ajouter du pooling de connexions, réduire max_connections pour forcer du backpressure, et mettre en cache le trafic anonyme.
  • Symptôme : beaucoup de requêtes bloquées sur « Waiting for table metadata lock ».
    Cause racine : DDL exécuté en période de pointe, bloqué par de longues transactions (ou bloquant d’autres).
    Correction : Arrêter les DDL pendant les pics ; trouver et terminer les transactions longues ; planifier des changements de schéma en ligne hors-peak.
  • Symptôme : pics corrélés avec %util disque proche de 100 % et await élevé ; la DB est « lente partout ».
    Cause racine : saturation IO et buffer pool insuffisant ; pression redo/checkpoint.
    Correction : Augmenter le buffer pool, tuner la taille des redo logs et le flushing, réduire l’amplification d’écritures (transients), upgrader le stockage ou isoler le disque DB.
  • Symptôme : un seul type de page (recherche, pages de catégorie) provoque des timeouts alors que d’autres pages vont bien.
    Cause racine : requêtes lentes spécifiques (souvent des jointures sur wp_postmeta) sans index ou utilisant filesorts/tables temporaires.
    Correction : Capturer les requêtes lentes, ajouter des indexes ciblés, ou modifier les patterns des plugins/thèmes. Ne pas « optimiser tout » au pif.
  • Symptôme : Après « migration vers MariaDB/MySQL », les choses se sont légèrement détériorées, pas catastrophiquement.
    Cause racine : changements de plan d’exécution de l’optimiseur, différences de collation, ou defaults mal appariés ; dashboards incomplets.
    Correction : Comparer les plans EXPLAIN, valider les requêtes critiques, standardiser collations/charsets, mettre à jour les exporters et seuils d’alerte.
  • Symptôme : Les réplicas vont bien mais le primaire meurt lors des pics.
    Cause racine : Hotspot d’écritures sur le primaire (options/transients/sessions) ; les réplicas n’aident pas les écritures.
    Correction : Réduire les écritures (cache d’objet, désactiver wp-cron via web, ajuster le comportement des transients), envisager de splitter les workloads, et optimiser les tables chaudes.

Checklists / plan étape par étape

During the incident (stop the 504s)

  1. Confirmer la source du timeout : Nginx upstream timeouts vs PHP-FPM vs DB.
  2. Freeze des changements : pas de mises à jour de plugins, pas de changements de schéma, pas de « quick fixes » dans wp-admin.
  3. Réduire la charge dynamique :
    • Activer/étendre le microcache Nginx pour le trafic anonyme si vous l’avez.
    • Limiter les endpoints abusifs (search, xmlrpc.php, wp-login.php) si c’est le pattern.
    • Désactiver wp-cron via les hits web ; le lancer via cron système si possible.
  4. Limiter la concurrence : maintenir les workers PHP-FPM à un nombre que la DB peut servir avec une latence acceptable.
  5. Trouver et tuer les bloqueurs : DDL longs, verrous métadonnées, requêtes hors de contrôle.
  6. Capturer des preuves : échantillons de processlist, extrait du slow log, status InnoDB, iostat.

After the incident (make it harder to repeat)

  1. Mettre en place du pooling de connexions si vous observez du churn de threads ou beaucoup de connexions lors des pics.
  2. Corriger l’autoload de wp_options : auditer et réduire ; supprimer les blobs autoload oversizés.
  3. Ajouter/valider des index pour les requêtes lentes principales. Ne pas deviner ; mesurer.
  4. Dimensionner correctement InnoDB : buffer pool, redo logs, comportement de flush selon le stockage.
  5. Améliorer l’observabilité : garantir que les métriques correspondent au moteur, dashboards montrant requêtes actives et lock waits.
  6. Pratiquer l’hygiène de pics : pas de changements de schéma pendant des pics prévus ; répéter les rollbacks.

FAQ

1) MariaDB est-elle plus rapide que MySQL pour WordPress ?

Parfois, dans des scénarios de concurrence spécifiques — surtout si le thread pool de MariaDB est utilisé. Mais les déterminants majeurs sont le caching, la qualité des requêtes, la taille du buffer pool et le contrôle des tempêtes de connexions.

2) Changer de moteur va-t-il régler mes 504 ?

Rarement par lui-même. La plupart des 504 lors des pics viennent d’une concurrence non bornée, de tables chaudes (wp_options), de requêtes lentes ou de saturation IO. Changer de moteur sans corriger ces points est un mouvement latéral avec de nouveaux modes de panne.

3) Quelle est la meilleure correction unique pour les pics WordPress ?

Cachez agressivement le trafic anonyme et limitez la concurrence dynamique. Si votre edge peut servir 80–95 % des requêtes sans toucher PHP/DB, les pics deviennent ennuyeux.

4) Dois-je augmenter max_connections pour stopper les erreurs « too many connections » ?

Seulement si vous avez prouvé que la DB peut gérer plus de concurrence. Sinon vous échangez une mort rapide contre une mort lente : plus de travail en file, plus de contention, plus de pression mémoire, plus de 504.

5) ProxySQL vaut-il le coup pour WordPress ?

Si vous avez des pics fréquents et beaucoup de connexions courtes, oui. Il peut lisser les tempêtes de connexions, permettre le routage vers des replicas et vous donner un point de contrôle. Il ajoute aussi de la complexité ; exploitez-le comme un vrai tier, pas comme un jouet sidecar.

6) Les réplicas aident-ils avec les 504 ?

Ils aident si les lectures sont votre goulot et si vous pouvez envoyer des lectures aux replicas en toute sécurité. Ils n’aident pas les hotspots d’écriture, la contention de verrous sur le primaire ou les écritures lentes liées au disque.

7) Pourquoi le CPU semble OK alors que le site timeoute ?

Parce que la latence peut être dominée par l’attente : attente IO, attente de verrous, thrash d’ordonnancement, flushing. Votre graphe CPU n’est pas un oracle de vérité ; c’est un indice.

8) Que dois-je tuner d’abord dans InnoDB pour WordPress ?

Taille du buffer pool (pour que les lectures tiennent en RAM), dimensionnement des redo logs adapté au débit d’écritures, et des réglages de flushing sensés. Ensuite concentrez-vous sur les requêtes/indexs et le bloat autoload — ce sont souvent les vrais coupables.

9) MySQL 8 est-il un choix plus sûr pour la compatibilité ?

Si vos outils et votre écosystème supposent les sémantiques de MySQL 8, oui, c’est souvent plus simple opérationnellement. MariaDB est compatible dans de nombreux cas, mais pas identique ; traitez-la comme un produit à part et validez les comportements.

Prochaines étapes à faire cette semaine

  1. Mesurer un pic : capturer des échantillons de processlist, slow query logs (brièvement) et stats IO pendant le pic. Ne pas tuner à l’aveugle.
  2. Contrôler la concurrence : fixer des caps PHP-FPM basés sur la capacité DB ; envisager un pooler si les connexions churnent.
  3. Corriger les hotspots WordPress : réduire la taille des options autoload, auditer les plugins qui frappent wp_postmeta, et ajouter les indexes manquants.
  4. Rendre le caching indispensable : page cache en bordure pour utilisateurs anonymes, cache d’objet pour les chemins lourds en DB.
  5. Choisir votre moteur selon l’opération : prenez celui que votre équipe sait monitorer correctement, upgrader en sécurité et récupérer rapidement. La performance est une fonctionnalité ; la récupération est un mode de vie.
← Précédent
ZFS logbias : latence vs débit — choisissez ce dont vous avez vraiment besoin
Suivant →
Lectures séquentielles ZFS — réglages pour un débit de streaming maximal

Laisser un commentaire