Vous ne « manquez » pas de base de données. Vous manquez de quelque chose d’ennuyeux : RAM, IOPS, descripteurs de fichiers, temps CPU ou patience.
Sur un VPS 8 Go, la marge entre « ça va » et « pourquoi le paiement expire » est plus mince que le sarcasme de votre canal d’incidents.
Ceci est un guide terrain pour monter en charge les clients en toute sécurité sur MariaDB et PostgreSQL quand on ne peut pas simplement jeter du matériel sur le problème.
Nous aborderons les limites de connexion, les calculs mémoire, la réalité de l’I/O, les pièges qui n’apparaissent qu’en charge et les pratiques qui vous évitent des ennuis.
Cadre du problème : ce que « monter en charge des clients » casse réellement
« Monter en charge des clients » signifie généralement « augmenter le nombre de connexions ou de requêtes simultanées ». Ça ressemble à un problème réseau.
Ce ne l’est pas. C’est un problème de comptabilité des ressources.
Le schéma d’échec typique sur un VPS 8 Go ressemble à ceci :
- Le trafic augmente, les serveurs applicatifs ouvrent plus de connexions DB « au cas où ».
- La base de données les accepte (parce que les valeurs par défaut sont généreuses ou vous avez augmenté
max_connectionsà la hâte). - La mémoire augmente par connexion/session. Le changement de contexte augmente. Les caches se retrouvent compressés.
- Les I/O montent en flèche parce que le working set ne tient plus en RAM.
- La latence augmente ; les timeouts applicatifs déclenchent des retries ; les retries créent plus de connexions ; vous vous inventez un déni de service auto-infligé.
Sur un VPS, il y a une autre torsion : steal CPU, voisins bruyants, crédits de burst, et un stockage qui « ressemble à un SSD » mais se comporte comme une clé USB triste au pire moment.
Une citation qu’il vaut la peine de coller près de votre tableau de bord : L’espoir n’est pas une stratégie.
— General Gordon R. Sullivan.
Ce n’est pas une citation de base de données, mais c’est la meilleure citation de production pour la montée en charge des bases de données.
La question centrale n’est pas « MariaDB ou PostgreSQL est plus rapide ? ». C’est :
lequel vous permettra d’imposer des limites sensées et un comportement prévisible à mesure que les clients montent en charge, avec une RAM contrainte et des disques réalistes.
Faits et histoire qui comptent en production
Ce ne sont pas des anecdotes pour le quiz du samedi soir. Elles expliquent pourquoi les valeurs par défaut, les comportements et les outils opérationnels ressemblent à ce qu’ils sont.
-
Le modèle de processus de PostgreSQL est historiquement « un backend par connexion ».
Ce design facilite l’isolation et le débogage, mais vous pousse vers le pooling des connexions à mesure que la concurrence augmente. -
L’approche thread-per-connection de MySQL a façonné l’histoire de la concurrence de MariaDB.
Elle peut gérer beaucoup de connexions, mais la mémoire par session peut quand même écraser une machine 8 Go si vous la laissez faire. -
MariaDB existe à cause de l’acquisition d’Oracle de MySQL (vers 2009–2010).
Ce fork a créé deux écosystèmes et deux ensembles d’hypothèses opérationnelles qui diffèrent encore aujourd’hui. -
InnoDB est devenu le moteur de stockage par défaut longtemps après l’ère MyISAM.
Beaucoup d’articles de tuning MySQL sont fossilisés de l’ère MyISAM et sont activement nuisibles sur des charges InnoDB modernes. -
Le MVCC de PostgreSQL a « l’encombrement » (bloat) comme réalité opérationnelle.
Le vacuum n’est pas optionnel si vous mettez à jour/supprimez beaucoup ; c’est le ramassage des ordures pour vos tables. -
InnoDB de MariaDB a aussi MVCC et purge, mais les leviers opérationnels diffèrent.
L’histoire compte : les réglages ne correspondent pas un pour un, et les symptômes trompent souvent lors des migrations. -
Le WAL de PostgreSQL et les redo/binlogs de MariaDB transforment tous deux les écritures aléatoires en I/O à peu près séquentielle.
Mais le comportement de fsync, les checkpoints et les « flush storms » varient encore selon le moteur et la configuration. -
PostgreSQL a introduit d’importantes améliorations de requêtes parallèles ces dernières années.
Cela aide l’analytique, mais peut vous surprendre sur de petites machines si les workers parallèles consomment CPU et mémoire pendant les pics OLTP. -
Le thread pool de MariaDB existe précisément parce que le thread-per-connection atteint des limites de montée en charge.
C’est un des rares réglages « activez-le, puis validez » qui peut réellement changer le comportement de concurrence.
Blague n°1 : Une base de données acceptera toujours votre changement max_connections=2000. C’est comme un bambin acceptant un troisième expresso.
Comment MariaDB et PostgreSQL dépensent vos 8 Go
Le budget VPS 8 Go : ce que vous avez vraiment
« 8 Go de RAM » c’est du marketing. En production, vous réservez de l’espace pour le noyau, le cache du système de fichiers, les services d’arrière-plan, et le fait que l’utilisation mémoire augmente en pointe.
Si vous exécutez uniquement la base de données, vous pouvez allouer en sécurité ~5–6 Go aux réglages mémoire de la base.
Si vous exécutez appli + DB sur le même VPS (évitez si possible), réduisez cela drastiquement.
Vous achetez aussi de l’I/O, pas seulement de la RAM. La plupart des bugs de latence attribués à la « performance de la base » sont en réalité des performances de stockage et de mise en file.
PostgreSQL : prévisible, mais gourmand en connexions
La mémoire de PostgreSQL se répartit globalement en :
- Mémoire partagée : principalement
shared_buffers. - Mémoire par session / par opération :
work_mem,maintenance_work_mem, tris, hash, buffers temporaires. - Cache de pages OS : PostgreSQL s’appuie fortement dessus pour de bonnes performances réelles.
Le tuérre sur les petites machines n’est pas shared_buffers. C’est la multiplication de l’overhead par connexion plus les allocations mémoire par requête.
Si vous autorisez des centaines de connexions directes depuis les applis, vous pariez votre uptime sur « elles ne feront pas toutes des requêtes coûteuses en même temps ».
Elles le feront. Un lundi. Pendant la paie. Ou quand quelqu’un publie un nouveau rapport.
MariaDB (InnoDB) : buffer pool et surprises par thread
Pour MariaDB (charges InnoDB), la mémoire est typiquement dominée par :
- InnoDB buffer pool : votre cache principal (
innodb_buffer_pool_size). - Buffers par connexion : sort buffer, join buffer, tables temporaires, buffers réseau.
- Caches de threads et internes : particulièrement visibles quand le churn de connexions est élevé.
MariaDB peut sembler « correcte » jusqu’à ce qu’une requête exige de gros sort/join buffers sur de nombreux threads et que vous fassiez une halte soudaine dans le swap.
Le swap sur un disque VPS génère une panne au ralenti.
Monter en charge les clients en sécurité, c’est surtout contrôler la concurrence
Les deux bases peuvent gérer un débit sérieux sur 8 Go si vous faites une chose : limiter la concurrence et lisser la charge.
Pooling de connexions, limites sensées et discipline de requêtes battent le tuning héroïque à chaque fois.
Playbook de diagnostic rapide (premier/deuxième/troisième)
Quand la latence augmente, vous avez besoin d’une boucle de triage rapide qui vous indique où est la limite : CPU, mémoire, verrous ou I/O.
Ne « tunez » pas. Diagnostiquez.
Premier : l’hôte est-il malade ?
- Moyenne de charge vs nombre de CPU : si la charge est élevée et que le CPU est inoccupé, vous attendez de l’I/O.
- Activité de swap : tout swap soutenu pendant le pic signifie que vous perdez.
- Latence disque : si
awaitest élevé, la base est innocente ; le stockage est la scène du crime.
Deuxième : la base est-elle mise en file ou bloquée ?
- PostgreSQL : vérifiez
pg_stat_activitypour les événements d’attente et les requêtes bloquées. - MariaDB : vérifiez processlist et InnoDB status pour les attentes de verrous et les deadlocks.
- Orages de connexions : timeouts + retries produisent une charge auto-infligée. Cherchez des sauts soudains du nombre de connexions.
Troisième : est-ce une requête mauvaise ou un tuning systémique ?
- Logs de requêtes lentes (les deux) montrent si quelques requêtes dominent le temps.
- Taux de hit du cache vous disent si vous êtes I/O bound à cause d’un cache insuffisant ou d’index manquants.
- Autovacuum / purge : les problèmes apparaissent comme des tables/index qui grossissent, une I/O en hausse et une « lenteur aléatoire ».
Cet ordre importe car le correctif le plus rapide est souvent hors de la base : réduire la concurrence client, arrêter les retries ou limiter les jobs d’arrière-plan.
12+ tâches pratiques avec commandes, signification des sorties, décisions
Voici les vérifications que j’exécute sur un VPS 8 Go avant de toucher aux paramètres de tuning. Chaque tâche inclut : la commande, la signification de la sortie et la décision qu’elle entraîne.
Tâche 1 : Confirmer la mémoire réelle et la pression de swap
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 7.7Gi 5.9Gi 220Mi 120Mi 1.6Gi 1.3Gi
Swap: 2.0Gi 1.1Gi 900Mi
Signification : « available » est ce que vous pouvez encore utiliser sans swap. Un swap déjà utilisé est un avertissement ; le swap qui bouge activement est la vraie alarme.
Si l’utilisation du swap augmente pendant la charge, la base va piétiner.
Décision : Si le swap est non négligeable sous charge, réduisez le nombre de connexions, baissez les réglages mémoire par session ou ajoutez de la RAM avant de « optimiser les requêtes ».
Tâche 2 : Vérifier le swap actif (la casserole lente de l’incident)
cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 1 118000 210000 65000 980000 120 240 3100 4200 900 2100 18 6 32 44 0
3 2 119200 190000 64000 970000 300 500 2800 3900 1000 2500 20 7 25 48 0
Signification : si/so au-dessus de ~0 soutenu signifie que vous swappez. wa élevé signifie que le CPU attend l’I/O.
Décision : Arrêtez l’hémorragie : limitez la concurrence (pool), baissez les multiplicateurs mémoire DB, désactivez les rapports coûteux en période de pointe ou migrez vers un stockage plus rapide.
Tâche 3 : Mesurer la latence disque et les files d’attente
cr0x@server:~$ iostat -xz 1 3
Linux 6.8.0 (server) 12/31/2025 _x86_64_ (2 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
14.20 0.00 6.50 28.10 0.80 50.40
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %util await
vda 85.0 120.0 4200.0 9800.0 0.0 2.0 96.0 18.5
Signification : %util proche de 100% avec await à deux chiffres signifie que le disque est saturé.
Sur de nombreuses plateformes VPS, c’est votre goulot d’étranglement.
Décision : Corrigez l’I/O : réduisez l’amplification d’écriture (indexes, tuning autovacuum/purge), déplacez les logs vers un stockage plus rapide ou passez à un niveau de disque supérieur. Ne faites pas monter les caches à l’aveugle.
Tâche 4 : Vérifier l’espace du système de fichiers et la pression sur les inodes
cr0x@server:~$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 80G 62G 15G 81% /
tmpfs 3.9G 0 3.9G 0% /dev/shm
cr0x@server:~$ df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/vda1 5242880 220000 5022880 5% /
Signification : Les disques presque pleins nuisent aux performances DB et peuvent bloquer les écritures (WAL/redo/binlog ne peuvent pas croître).
Décision : Gardez une marge d’espace libre (j’aime 20%+ sur les systèmes chargés). Si vous êtes juste, vous êtes à un incident d’un « passage en lecture seule ».
Tâche 5 : Vérifier les limites de descripteurs de fichiers (tueur silencieux de connexions)
cr0x@server:~$ ulimit -n
1024
Signification : 1024 est bas pour une base qui peut ouvrir beaucoup de fichiers (tables, index, segments WAL) plus les sockets.
Décision : Augmentez les limites via l’unité systemd ou limits.conf. Si votre DB atteint les limites FD, les clients voient des erreurs de connexion qui semblent « aléatoires ».
Tâche 6 (PostgreSQL) : Compter les connexions et voir qui les accapare
cr0x@server:~$ sudo -u postgres psql -X -c "SELECT state, count(*) FROM pg_stat_activity GROUP BY 1 ORDER BY 2 DESC;"
state | count
---------+-------
idle | 140
active | 12
| 3
(3 rows)
Signification : Beaucoup de connexions idle signifie généralement que l’application accumule des connexions. C’est un overhead mémoire et de processus qui ne sert à rien.
Décision : Utilisez un pooler (PgBouncer) et réduisez les connexions directes. Définissez des tailles de pool applicatives sensées, ne laissez pas chaque pod ouvrir 50 sessions « parce que les valeurs par défaut ».
Tâche 7 (PostgreSQL) : Trouver les requêtes bloquées et les chaînes de verrous
cr0x@server:~$ sudo -u postgres psql -X -c "SELECT pid, wait_event_type, wait_event, state, left(query,80) AS q FROM pg_stat_activity WHERE wait_event IS NOT NULL ORDER BY pid;"
pid | wait_event_type | wait_event | state | q
------+-----------------+---------------+---------+----------------------------------------------
2314 | Lock | relation | active | UPDATE orders SET status='paid' WHERE id=$1
2451 | Lock | transactionid | active | DELETE FROM carts WHERE user_id=$1
(2 rows)
Signification : Attente de verrous signifie que votre concurrence se fait la guerre. Ajouter des connexions aggrave la situation.
Décision : Identifiez la transaction bloquante (souvent longue ou idle-in-transaction). Corrigez la portée des transactions dans l’appli ; ajoutez des index pour réduire le temps de verrou ; planifiez les migrations en sécurité.
Tâche 8 (PostgreSQL) : Vérifier l’efficacité du cache (êtes-vous I/O bound ?)
cr0x@server:~$ sudo -u postgres psql -X -c "SELECT datname, blks_hit, blks_read, round(100.0*blks_hit/(blks_hit+blks_read+1),2) AS hit_pct FROM pg_stat_database ORDER BY hit_pct ASC;"
datname | blks_hit | blks_read | hit_pct
-----------+----------+-----------+---------
appdb | 89213321 | 5120032 | 94.55
postgres | 1200032 | 12033 | 98.99
(2 rows)
Signification : 94–95% peut convenir pour certains workloads, être terrible pour d’autres. Si le hit% baisse en charge et que la latence disque augmente, vous êtes à court de cache ou votre working set a grandi.
Décision : Ajoutez de la RAM ou réduisez le working set (meilleurs index, moins de scans complets). Ne montez pas shared_buffers à 6 Go en affamant le cache OS.
Tâche 9 (PostgreSQL) : Vérifier la pression de vacuum (taxe de bloat)
cr0x@server:~$ sudo -u postgres psql -X -c "SELECT relname, n_dead_tup, n_live_tup, last_autovacuum FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 5;"
relname | n_dead_tup | n_live_tup | last_autovacuum
-------------+------------+------------+----------------------------
events | 8200032 | 41000211 | 2025-12-31 10:22:14+00
sessions | 1200033 | 9000123 | 2025-12-31 10:18:01+00
carts | 450012 | 1500032 | 2025-12-31 09:59:44+00
(3 rows)
Signification : Des tuples morts qui s’accumulent signifient plus de lectures disque, des index plus volumineux, des requêtes plus lentes. L’autovacuum peut être sous-dimensionné ou bloqué par de longues transactions.
Décision : Corrigez les longues transactions, ajustez autovacuum par table et envisagez le partitionnement pour les tables à forte écriture.
Tâche 10 (MariaDB) : Vérifier les connexions actuelles et l’utilisation max
cr0x@server:~$ sudo mariadb -e "SHOW GLOBAL STATUS LIKE 'Threads_connected'; SHOW GLOBAL STATUS LIKE 'Max_used_connections'; SHOW VARIABLES LIKE 'max_connections';"
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_connected | 210 |
+-------------------+-------+
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| Max_used_connections | 398 |
+----------------------+-------+
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 500 |
+-----------------+-------+
Signification : Vous avez déjà atteint 398 connexions. Ce n’est pas « capacité », c’est « vous flirtez avec l’effondrement mémoire ».
Décision : Implémentez du pooling côté appli ou utilisez le thread pool de MariaDB ; réduisez max_connections à un nombre sûr ; imposez des timeouts et tuez les sleepers inactifs.
Tâche 11 (MariaDB) : Repérer les connexions en sommeil et les longues exécutions
cr0x@server:~$ sudo mariadb -e "SHOW PROCESSLIST;"
Id User Host db Command Time State Info
1203 app 10.0.1.12:44218 appdb Sleep 512 NULL
1210 app 10.0.1.12:44276 appdb Query 9 Sending data SELECT * FROM events WHERE user_id=?
1311 app 10.0.1.13:51022 appdb Sleep 611 NULL
Signification : Beaucoup de Sleep avec Time élevé est de l’accaparement de connexions. L’état Sending data signifie souvent un scan et le renvoi de lignes (ou une attente disque).
Décision : Réduisez les tailles de pool applicatives, ajoutez des limites de durée de vie des connexions, activez le log des requêtes lentes et corrigez les index manquants.
Tâche 12 (MariaDB) : Vérifier la taille et la pression du InnoDB buffer pool
cr0x@server:~$ sudo mariadb -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads'; SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';"
+-------------------------+------------+
| Variable_name | Value |
+-------------------------+------------+
| innodb_buffer_pool_size | 4294967296 |
+-------------------------+------------+
+--------------------------+---------+
| Variable_name | Value |
+--------------------------+---------+
| Innodb_buffer_pool_reads | 8200332 |
+--------------------------+---------+
+----------------------------------+------------+
| Variable_name | Value |
+----------------------------------+------------+
| Innodb_buffer_pool_read_requests | 932003312 |
+----------------------------------+------------+
Signification : Le ratio de lectures disque vs demandes de lecture indique les misses de cache. Sur un VPS 8 Go, 4 Go de buffer pool peut être raisonnable, mais pas si les buffers par thread sont énormes.
Décision : Maintenez un buffer pool substantiel (souvent 50–70% de la RAM si DB-only), mais auditez d’abord les buffers par connexion et le nombre de connexions.
Tâche 13 (MariaDB) : Détecter les débordements de tables temporaires (marteau disque silencieux)
cr0x@server:~$ sudo mariadb -e "SHOW GLOBAL STATUS LIKE 'Created_tmp%tables';"
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Created_tmp_disk_tables | 2200033 |
| Created_tmp_tables | 3100044 |
+-------------------------+----------+
Signification : Une forte proportion de tables temporaires sur disque indique des requêtes qui trient/regroupent de grands jeux de données sans index adéquat, ou des limites tmp table trop petites.
Décision : Corrigez d’abord les requêtes/index. Ensuite seulement envisagez d’ajuster tmp_table_size/max_heap_table_size, car les augmenter élève le risque mémoire par session.
Tâche 14 : Confirmer le backlog des sockets réseau et les pseudo-SYN flood
cr0x@server:~$ ss -s
Total: 1180
TCP: 942 (estab 410, closed 410, orphaned 0, timewait 110)
Transport Total IP IPv6
RAW 0 0 0
UDP 8 6 2
TCP 532 410 122
INET 540 416 124
FRAG 0 0 0
Signification : Si les connexions établies montent et que timewait croît rapidement, votre appli se reconnecte agressivement ou un load balancer se comporte mal.
Décision : Corrigez le comportement client (pool, keepalive, timeouts). Les bases détestent le churn de connexions presque autant que les humains détestent un on-call surprise.
Tâche 15 : Confirmer le steal CPU (réalité VPS)
cr0x@server:~$ mpstat 1 3
Linux 6.8.0 (server) 12/31/2025 _x86_64_ (2 CPU)
12:00:01 AM all %usr %nice %sys %iowait %irq %soft %steal %idle
12:00:02 AM all 22.00 0.00 10.00 18.00 0.00 1.00 6.00 43.00
12:00:03 AM all 20.00 0.00 9.00 19.00 0.00 1.00 7.00 44.00
Signification : %steal indique que l’hyperviseur vous retire du CPU. Si le steal monte pendant les pics, la lenteur de votre base est littéralement le travail de quelqu’un d’autre.
Décision : Envisagez une classe VPS meilleure ou du CPU dédié. Le tuning ne réparera pas les cycles volés.
Schémas sûrs pour monter en charge davantage de clients
Schéma 1 : Le pooling de connexions n’est pas optionnel sur 8 Go
Si vous exécutez PostgreSQL et attendez de nombreux clients concurrents, placez un pooler devant. Point final.
Même si vous « n’avez que » 100 instances d’appli. Surtout alors.
Pour PostgreSQL, le pooling externe (PgBouncer) est le mouvement standard car il réduit les processus backend tout en conservant la concurrence côté client.
Pour MariaDB, beaucoup de stacks utilisent le pooling côté application ; le thread pool de MariaDB peut aider, mais ce n’est pas gratuit.
Le modèle mental sûr : les clients peuvent être nombreux, la concurrence côté serveur doit être petite et contrôlée.
Vous voulez une file d’attente que vous contrôlez, pas une ruée de ressources que le noyau contrôle.
Schéma 2 : Définissez des plafonds stricts et faites-les respecter
« Illimité » est la façon de provoquer une défaillance en cascade. Limitez :
- Les connexions à la base (
max_connections/max_connections). - Les connexions par utilisateur ou par appli (rôles, users, règles firewall, réglages du pooler).
- Le temps d’exécution des requêtes (PostgreSQL
statement_timeout, MariaDBmax_execution_timepour SELECT, plus timeouts applicatifs). - Le temps idle-in-transaction (PostgreSQL
idle_in_transaction_session_timeoutest un sauveur).
Schéma 3 : Protégez le cache en maintenant faibles les multiplicateurs mémoire
L’erreur de tuning la plus commune sur 8 Go est de dépenser toute la RAM en « mémoire base de données » en oubliant que :
les buffers par connexion se multiplient, et le cache OS n’est pas de la « mémoire gaspillée ».
PostgreSQL : si vous réglez work_mem haut et autorisez de nombreuses sessions actives, vous pouvez allouer bien plus de mémoire que vous ne possédez.
MariaDB : si vous gonflez les sort/join buffers et laissez beaucoup de threads, vous pouvez faire la même chose, seulement avec une comptabilité moins évidente.
Schéma 4 : Rendez les écritures ennuyeuses
Sur de petits disques VPS, les écritures causent la plupart des douleurs :
fsync WAL/redo, checkpoints, flush en arrière-plan, temp tables, vacuum, maintenance d’index.
Votre objectif est un comportement d’écriture stable, pas « rapide jusqu’à ce que ça coince ».
- Gardez les transactions courtes.
- Regroupez les jobs d’arrière-plan hors-peak ou throttlez-les.
- Ne créez pas trois index à midi sur une table occupée.
- Utilisez des réglages de durabilité sensés ; ne désactivez pas fsync à moins d’accepter la perte de données.
Schéma 5 : Scalez les lectures et écritures différemment
Sur un seul VPS 8 Go, le scaling vertical est limité. Alors vous scalez la forme :
- Lectures : cache, réplicas de lecture, optimisation des requêtes, indexation, modèles de lecture dénormalisés si nécessaire.
- Écritures : réduisez l’amplification d’écriture, partitionnez les grandes tables à churn, archivez les anciennes données, évitez les opérations « mettre à jour chaque ligne ».
PostgreSQL et MariaDB peuvent répliquer. Les compromis opérationnels diffèrent, mais le principe tient : n’attendez pas qu’une seule machine fasse un travail infini.
Schéma 6 : Observez la base depuis l’extérieur
Les stats internes sont excellentes. Les symptômes externes sont meilleurs pour le triage :
- p95/p99 latence des requêtes
- attente disque et utilisation
- activité de swap
- taux de connexion (nouvelles connexions/sec)
- comportement checkpoint/flush
Si vous ne le voyez pas, vous « résoudrez » le mauvais problème. C’est ainsi que les incidents se répètent avec de meilleurs dashboards.
Blague n°2 : Désactiver la durabilité pour « améliorer la performance » revient à enlever vos freins pour gagner une course. Vous arriverez plus vite, brièvement.
Trois mini-récits tirés de la vie en entreprise
Mini-récit 1 : L’incident causé par une fausse hypothèse (connexions PostgreSQL)
Une équipe SaaS de taille moyenne est passée d’une base gérée à une instance PostgreSQL auto-hébergée sur un VPS 8 Go pour réduire les coûts.
La migration s’est bien passée. La première semaine était calme. Tout le monde s’est félicité et est retourné à l’ajout de fonctionnalités.
Puis ils ont embarqué un gros client. Le trafic a doublé. La latence a augmenté, mais uniquement en heures ouvrées. L’équipe a supposé que c’étaient « juste des requêtes plus lourdes »
et a commencé à chasser des index. Cela a aidé un peu. Puis le vrai incident est arrivé : l’appli a commencé à renvoyer des 500 intermittents, et le graphe CPU DB semblait étrangement calme.
La fausse hypothèse était subtile : ils pensaient « PostgreSQL peut gérer beaucoup de connexions comme notre ancien système ».
Leur couche appli utilisait un pool de connexions par défaut de 50 par instance. Avec quelques instances, c’était déjà élevé. Avec l’auto-scaling, ça a explosé.
La machine a exécuté des centaines de processus backend, a mangé de la mémoire, a forcé le noyau à récupérer le cache, et a transformé chaque cache miss en une fête d’attente I/O.
La correction n’était pas un index intelligent. C’était ennuyeux : réduire les tailles de pool appli, déployer PgBouncer, plafonner les connexions serveur et définir des timeouts pour que les sessions inactives ne restent pas.
Ils ont aussi ajusté le comportement de retry pour éviter une boucle de rétroaction.
Après cela, l’optimisation des requêtes a vraiment fonctionné — parce que le système ne se détruisait plus lui-même.
La leçon est restée : sur de petites machines, la gestion des connexions est la gestion de la performance.
Mini-récit 2 : L’optimisation qui s’est retournée contre eux (réglages mémoire MariaDB)
Une autre société utilisait MariaDB pour un tableau de bord analytique interne sur une VM 8 Go. C’était surtout orienté lecture, avec des chargements batch périodiques.
Quelqu’un a lu un vieux guide de tuning et a décidé que « des buffers plus grands sont toujours mieux ». Ils ont augmenté les sort/join buffers par thread et élargi les limites des tables temporaires.
Sur un système calme, c’était excellent. Les requêtes qui déversaient auparavant sur disque devenaient plus rapides. Le tableau était plus réactif.
Le changement a été promu en production sans tests de charge parce que le système « n’était pas critique ». Cette phrase vieillit mal.
Lorsque le prochain chargement batch s’est lancé, plusieurs rapports concurrents et jobs ETL ont démarré. Chaque job a ouvert plusieurs connexions.
Chaque connexion pouvait maintenant allouer de gros buffers. L’utilisation mémoire a grimpé vite, le noyau a commencé à swapper, et la machine est entrée dans l’état classique :
CPU souvent idle, moyenne de charge haute, requêtes expirant, et tout le monde accusant « la base de données lente ».
Le rollback des changements mémoire a stabilisé le système. La correction à long terme a été plus disciplinée :
mettre un plafond sur les jobs lourds concurrents, corriger les pires requêtes et index, et garder la mémoire par connexion petite pour que la concurrence ne se multiplie pas en chaos.
À retenir : les réglages mémoire peuvent être des améliorateurs de performance ou des boutons d’auto-destruction. Sur 8 Go, ils sont souvent les deux.
Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la journée (marge disque et hygiène WAL/binlog)
Une équipe exécutait une charge mixte — transactions plus jobs d’arrière-plan — sur un seul VPS en attendant l’approbation budgétaire.
Pas idéal, mais la réalité a des opinions. Ils ont fait une chose de manière constante : des vérifications de capacité routinières et une marge disque conservatrice.
Ils ont suivi l’utilisation disque, les motifs de croissance WAL/binlog, et ont veillé à une rotation des logs sensée.
Ils ont aussi surveillé le lag de réplication (quand activé) et gardé une alerte stricte sur les seuils d’utilisation du système de fichiers.
Rien de tout cela n’était glamour. Personne n’a été promu pour ça.
Un jour, un déploiement a introduit un bug qui a généré bien plus d’écritures que d’habitude. WAL/binlogs ont commencé à croître.
Sur beaucoup d’équipes, c’est à ce moment que vous découvrez que votre disque est plein et que la base refuse les écritures.
Ils ne l’ont pas découvert ainsi. Une alerte s’est déclenchée tôt, avant la zone de danger.
L’on-call a throttlé le job fautif, mis en pause le rollout, et réduit le blast radius pendant que la DB restait saine.
Le postmortem a été court. La correction a été simple. L’impact client a été minime.
Les pratiques ennuyeuses n’empêchent pas tous les bugs. Elles empêchent les bugs de devenir des incidents.
Erreurs courantes : symptôme → cause racine → correction
1) « La DB est lente » mais le CPU est bas et la moyenne de charge est élevée
Symptôme : spikes de latence p95 ; CPU idle ; load élevé ; plaintes des utilisateurs.
Cause racine : attente I/O (saturation disque) ou swapping.
Correction : Vérifiez iostat/vmstat. Réduisez la concurrence, arrêtez le swap, passez à un meilleur disque, et coupez l’amplification d’écriture (index, tuning vacuum/purge).
2) Erreurs de connexion aléatoires pendant les pics de trafic
Symptôme : « trop de connexions », « impossible de se connecter », échecs intermittents.
Cause racine : pools d’appli non bornés + absence de pooling + limites FD basses.
Correction : Faites respecter les tailles de pool, utilisez PgBouncer pour PostgreSQL, fixez un max_connections réaliste, augmentez ulimit -n, et ajoutez du backpressure.
3) PostgreSQL devient plus lent sur des jours/semaines, puis « s’améliore magiquement » après maintenance
Symptôme : ralentissement progressif ; disque qui grossit ; plus de lectures ; logs autovacuum qui apparaissent.
Cause racine : bloat table/index dû au MVCC et vacuum insuffisant, souvent aggravé par de longues transactions.
Correction : Trouvez et éliminez les sessions longues/idle-in-transaction. Ajustez autovacuum par table. Planifiez vacuum/analyze où nécessaire.
4) MariaDB va bien jusqu’à ce qu’un rapport s’exécute, puis tout se bloque
Symptôme : Un tableau/rapport unique cause un ralentissement global ; spike des temp tables.
Cause racine : plan de requête mauvais + index manquant + tables temporaires sur disque + concurrence.
Correction : Utilisez le log des requêtes lentes, EXPLAIN, ajoutez des index, limitez les jobs de rapport concurrents, et évitez d’augmenter les buffers par thread comme pansement.
5) Le lag de réplication augmente pendant les heures de pointe
Symptôme : réplique de lecture en retard ; risque de basculement accru.
Cause racine : rafales d’écritures, fsync disque lent, grosses transactions, ou pression checkpoint/vacuum.
Correction : Réduisez les rafales d’écriture, découpez les grosses transactions, ajustez checkpoint/flush avec précaution, et assurez-vous que les réglages de réplication correspondent aux capacités disque.
6) Pics de latence toutes les quelques minutes comme un battement de cœur
Symptôme : arrêts réguliers, ralentissements périodiques, puis récupération.
Cause racine : checkpoints/flush storms (checkpoints PostgreSQL ; flush InnoDB) ou jobs programmés qui se chevauchent.
Correction : Lissez la charge d’écriture ; ajustez intervalle de checkpoint et completion_target (PostgreSQL) ; validez les réglages de flush InnoDB ; planifiez les jobs hors-peak.
7) « Nous avons augmenté les caches et c’était pire »
Symptôme : des réglages mémoire plus hauts entraînent plus de latence, pas moins.
Cause racine : affamer le cache OS, augmenter le risque de swap, ou augmenter la capacité d’allocation par session.
Correction : Reculez. Gardez de la marge. Faites des réglages mémoire proportionnels à la concurrence et aux charges, pas à l’espoir.
Listes de contrôle / plan étape par étape
Plan étape par étape : monter en charge des clients en sécurité sur un VPS 8 Go (soit l’une ou l’autre base)
- Séparez les responsabilités : si possible, gardez la DB sur son propre VPS. Si vous ne pouvez pas, budgétez mémoire et CPU pour la DB explicitement.
- Mesurez la base : attente disque, activité de swap, nombre de connexions, latence p95. Notez-les. Si vous ne le faites pas, vous vous disputerez plus tard.
- Définissez des limites strictes de connexions : plafonnez les connexions serveur et les tailles de pool applicatives. Évitez « l’autoscaling = connexions infinies ».
- Ajoutez du pooling : PgBouncer pour PostgreSQL ; pour MariaDB utilisez un pooling appli robuste et envisagez le thread pool si pertinent.
- Fixez des timeouts : tuez les sessions idle-in-transaction ; définissez des timeouts de requête pour les runaway ; utilisez des timeouts appli qui n’entraînent pas de retry storms.
- Protégez la RAM : gardez la mémoire par session petite ; réservez le cache OS ; évitez le swap. Ajoutez de la marge comme si vous le pensiez vraiment.
-
Activez la visibilité des requêtes lentes : slow query log (MariaDB) ou
pg_stat_statements(PostgreSQL) et loggez les requêtes lentes. - Corrigez les pires coupables en premier : ajoutez les index manquants, supprimez les N+1, éliminez les scans massifs non bornés.
- Stabilisez les écritures : évitez les transactions énormes ; regroupez les jobs d’arrière-plan ; ajustez checkpoint/flush prudemment et validez avec des métriques.
- Planifiez la croissance : si le nombre de clients continue d’augmenter, planifiez des réplicas de lecture, du partitionnement, ou passez à une machine plus grosse avant d’y être forcé en incident.
Liste spécifique PostgreSQL (VPS 8 Go)
- Utilisez un pooler si la concurrence client dépasse quelques dizaines.
- Gardez
shared_buffersmodéré (souvent autour de 1–2 Go sur 8 Go DB-only) ; laissez de la place pour le cache OS. - Réglez
work_memprudemment et souvenez-vous qu’il peut être utilisé plusieurs fois par nœud de requête. - Surveillez autovacuum : tuples morts, longues transactions, risque de freeze vacuum.
- Vérifiez les événements d’attente avant de changer les paramètres. Les verrous et les waits I/O nécessitent des corrections différentes.
Liste spécifique MariaDB (VPS 8 Go)
- Dimensionnez
innodb_buffer_pool_sizeréalistement (souvent 4–6 Go sur 8 Go DB-only, selon le nombre de connexions et les buffers par thread). - Gardez les buffers par thread sages ; évitez de « simplement augmenter sort buffer » sans calcul de concurrence.
- Activez et relisez régulièrement le slow query log ; corrigez les pires requêtes, pas les moyennes.
- Validez le comportement des temp tables et réduisez les tables temporaires disque en indexant et réécrivant les requêtes.
- Envisagez le thread pool lorsque vous avez beaucoup de connexions mais peu de CPU.
Règle pratique pour la capacité (pratique, pas parfaite)
Sur 8 Go, visez un petit nombre stable de requêtes actives en exécution (des dizaines, pas des centaines).
Laissez les clients faire la queue dans un pooler ou une file applicative plutôt que de laisser le noyau et la base se battre pour la mémoire et l’I/O.
FAQ
1) Lequel est « meilleur » sur un VPS 8 Go : MariaDB ou PostgreSQL ?
Si vous avez besoin de sémantiques strictes, d’une observabilité forte des waits/verrous et d’outils de planification de requêtes prévisibles, PostgreSQL offre généralement une meilleure expérience opérationnelle.
Si vous êtes profondément dans l’écosystème MySQL et que votre charge est OLTP simple avec un pooling discipliné, MariaDB peut très bien fonctionner.
Le plus grand prédicteur est si vous contrôlez le nombre de connexions et le comportement d’écriture.
2) Combien de connexions devrais-je autoriser ?
Moins que vous ne le pensez. Sur un VPS 8 Go, les connexions directes en centaines sont un chemin commun vers le swap et l’effondrement I/O.
Préférez une petite limite côté serveur (souvent des dizaines à bas centaines selon la charge) et mettez en file à la couche pooler/appli.
Mesurez la mémoire et la latence pendant le pic, puis ajustez.
3) Ai-je vraiment besoin de PgBouncer pour PostgreSQL ?
Si vous avez beaucoup d’instances applicatives, des connexions de courte durée ou un trafic en rafales : oui.
Le modèle de connexion de PostgreSQL rend le pooling la manière propre de monter en charge le nombre de clients sans multiplier les processus backend.
Si vous avez peu de connexions longues et pas de pics, vous pouvez vous en passer — jusqu’au jour où vous ne le pouvez plus.
4) MariaDB peut-elle gérer plus de connexions que PostgreSQL sans pooling ?
Elle peut gérer plus de threads que PostgreSQL ne gère de processus, mais « gérer » n’est pas synonyme de « rester rapide ».
La mémoire par connexion et les buffers par requête se multiplient toujours, et la contention augmente.
Le pooling et un comportement applicatif sensé comptent autant.
5) Dois-je augmenter shared_buffers (PostgreSQL) pour utiliser la plupart de la RAM ?
Non. PostgreSQL bénéficie fortement du cache OS. Sur 8 Go, un shared_buffers modéré fonctionne souvent mieux qu’un réglage agressif
parce qu’il laisse de la place pour le cache du système de fichiers et évite la pression mémoire. Validez avec les taux de hit cache et l’attente disque.
6) Dois-je régler innodb_buffer_pool_size à 75–80% de la RAM (MariaDB) ?
Seulement si la DB est le service principal et que vos nombres de connexions et buffers par thread sont maîtrisés.
Si vous avez une forte concurrence ou de gros buffers par session, 80% peut vous pousser dans le swap pendant les pics.
Démarrez conservateur, mesurez, puis augmentez.
7) Pourquoi les performances empirent-elles quand j’augmente la mémoire par requête (work_mem, sort/join buffers) ?
Parce que la concurrence transforme « par requête » en « par tout le monde ».
Plus de mémoire par requête peut aider une requête isolée, mais en charge cela augmente l’empreinte mémoire totale, comprime le cache et déclenche le swap.
Sur une petite RAM, vous voulez un comportement prévisible plutôt que la vitesse maximale d’une seule requête.
8) Quel est le premier signe que je suis I/O bound ?
Une latence qui augmente avec un CPU bas et un iowait élevé est le classique. iostat montre un %util élevé et un await élevé.
Les événements d’attente PostgreSQL montrent souvent des waits liés à l’I/O ; MariaDB peut indiquer « Sending data » alors qu’elle attend des lectures.
9) Est-il sûr de désactiver fsync ou d’assouplir la durabilité pour survivre à un pic ?
C’est une décision business déguisée en réglage. Si vous désactivez fsync, vous acceptez la perte de données en cas de crash.
Si vous avez besoin de débit, corrigez d’abord la concurrence, les requêtes et le stockage. N’assouplissez la durabilité que si vous pouvez expliquer le rayon d’impact aux parties prenantes sans cligner des yeux.
10) Quand arrêter le tuning et upgrader le VPS ?
Quand le disque est saturé en état stable, quand vous ne pouvez pas éviter le swap lors des pics malgré des limites disciplinées, ou quand le steal CPU domine.
Le tuning ne peut pas créer des IOPS ni récupérer du CPU volé. Monter en gamme est parfois l’optimisation la plus fiable.
Prochaines étapes réalisables cette semaine
- Choisissez une stratégie de connexion : pooler pour PostgreSQL, pooling applicatif strict pour MariaDB. Écrivez votre cible de concurrence serveur maximale.
- Exécutez les vérifications de diagnostic rapide pendant le pic :
vmstat,iostat, compte de connexions, attentes de verrous. - Activez la visibilité des requêtes lentes et corrigez les 3 pires coupables. Pas 30. Trois.
- Fixez des timeouts qui empêchent les sessions hors de contrôle et les tempêtes de retries.
- Créez un budget de capacité pour la RAM : cache partagé + overhead par connexion + marge pour le cache OS. Puis appliquez-le avec des limites.
- Planifiez la sortie : si la croissance se poursuit, décidez tôt si vous ajouterez un replica, passerez à une machine plus grosse ou séparerez les charges.
Sur un VPS 8 Go, vous ne gagnez pas en étant ingénieux. Vous gagnez en étant discipliné : moins de connexions, des écritures plus régulières et un refus de laisser les valeurs par défaut diriger l’architecture.