MySQL vs MariaDB sur VPS 2 Go : profils de réglage qui ne plantent pas

Cet article vous a aidé ?

Vous avez pris un VPS 2 Go parce que « ça suffit pour une petite appli ». Puis la base de données a commencé à se comporter comme une plante d’intérieur fragile : quelques connexions de plus, une requête lourde, et elle s’effondre — parfois en emportant tout le nœud avec elle. Vous n’avez pas besoin d’héroïsme. Vous avez besoin de budgets mémoire sensés, d’un E/S prévisible et de paramètres qui échouent de façon contrôlée plutôt qu’explosive.

Ceci est un guide de terrain pour faire tourner MySQL ou MariaDB sur une petite machine sans transformer le killer OOM du noyau en votre DBA principal. Je suis catégorique parce que la production n’est pas neutre : elle se fiche de vos benchmarks si votre serveur redémarre à 3 h du matin.

Choisir MySQL ou MariaDB sur 2 Go : les vrais points de décision

Sur un VPS 2 Go, « MySQL vs MariaDB » relève moins de l’idéologie que de l’ergonomie opérationnelle : les valeurs par défaut, les paquets et les champs de mines que vous préférez gérer.

Ma recommandation (tranchée, avec réserves)

  • Si vous avez besoin d’un comportement prévisible sur différents clouds et services managés : choisissez MySQL 8.0. C’est ce que la plupart des fournisseurs hébergés utilisent, et le savoir opérationnel est profond.
  • Si vous vous hébergez vous-même et voulez des réglages utiles pour les petites machines : MariaDB peut être plus amicale, surtout avec le thread pool (sur certaines builds) et un empaquetage raisonnable sur de nombreuses distributions.
  • Si vous dépendez des fonctionnalités de MySQL 8 (certains comportements JSON et de réplication, améliorations de l’optimiseur, outils officiels) : choisissez MySQL, paramétrez-le prudemment, et avancez.
  • Si vous voulez minimiser les surprises lors des mises à jour : choisissez ce que votre distribution supporte le mieux et figez la version. Sur 2 Go, la surprise coûte cher.

En termes de performance, n’importe lequel des moteurs saturera volontiers votre CPU minuscule et votre disque lent avant de décrocher un prix. Le véritable risque, c’est la mémoire : la base sait très bien dépenser tout ce que vous lui donnez, plus ce que vous avez oublié qu’elle pouvait dépenser.

Blague n°1 : Un VPS 2 Go avec une base de données non réglée, c’est comme un bagage cabine : on peut tout y mettre, jusqu’à ce que la fermeture éclair décide que vous l’avez offensée.

Faits et contexte intéressants qui comptent vraiment

Ce ne sont pas des anecdotes de quiz. Ce sont des éléments qui expliquent pourquoi les réglages diffèrent, pourquoi il y a des valeurs par défaut, et pourquoi « copiez ce my.cnf » est un piège.

  1. MariaDB a été forkée depuis MySQL après qu’Oracle a racheté Sun (2010) : ce fork n’était pas que politique — il a entraîné des divergences de valeurs par défaut, de fonctionnalités et parfois d’interprétation de la « compatibilité ».
  2. MySQL 5.6 a fait d’InnoDB le choix clair pour les charges sérieuses : le passage des recettes de l’ère MyISAM vers le tuning centré InnoDB a changé la notion de « sûr ».
  3. MySQL 8.0 a supprimé entièrement le query cache : d’anciens guides de performance recommandent encore des réglages du query cache ; sur MySQL 8 ces options n’existent plus, et sur MariaDB elles peuvent toujours vous nuire.
  4. Le buffer pool InnoDB est devenu le levier principal : la règle « donnez-lui 80 % de la RAM » a fait fureur sur des hôtes DB dédiés — sur un VPS 2 Go partagé, c’est imprudent.
  5. Le thread pooling a divergé : MariaDB a des implémentations de thread pool largement utilisées ; l’histoire de MySQL varie selon l’édition et la version. Sur de petits CPU, la gestion des threads impacte la latence de queue plus que vous ne le pensez.
  6. Le logging redo d’InnoDB a évolué : ajuster la capacité redo et le comportement de flush compte parce que les disques des petits VPS sont souvent réseau‑montés et sujettes à des rafales.
  7. Les valeurs par défaut Ubuntu/Debian ont changé au fil du temps : vous verrez des configurations de base différentes selon les versions majeures, ce qui signifie que « MySQL stock » n’est pas unique.
  8. L’OOM killer de Linux ne connaît pas votre SLA : il tue un processus. Souvent mysqld. Parfois votre appli. Dans tous les cas, votre canal d’incident s’allume.

Modèle mental pour VPS 2 Go : où va vraiment la mémoire

Sur les petites machines, la question n’est pas « quel est le réglage le plus rapide ? » mais « quel dommage maximal peut causer ce réglage à la pointe de la concurrence ? » Ce changement de perspective évite les plantages.

Budgétez votre RAM comme vous budgétez votre sommeil en astreinte

Avec 2 Go de RAM, vous n’avez pas 2 Go pour MySQL/MariaDB. Vous avez :

  • Noyau + cache de page + slab : typiquement quelques centaines de Mo, davantage sous charge d’E/S.
  • Services système (sshd, journald, cron, agent de monitoring) : 50–200 Mo.
  • Runtime de votre application (PHP‑FPM, Node, Java, workers Python) : de « raisonnable » à « pourquoi ça fait 900 Mo ».
  • La base de données : ce qui reste, et elle tentera de tout prendre.

Sur un VPS dédié à la BD 2 Go (pas d’appli), vous pouvez pousser la BD plus loin. Sur une machine partagée app+BD (commun dans les stacks bon marché), il faut réserver de la marge pour éviter que le noyau ne panique et tue des processus.

Les deux types de mémoire dans la base : globale et par‑connexion

La plupart des guides parlent des buffers globaux (buffer pool, log buffers). L’assassin caché est la mémoire par connexion :

  • sort buffers
  • join buffers
  • read buffers
  • tmp tables (en mémoire jusqu’à déversement)
  • stacks de thread

Chacun peut sembler petit. Multipliez par 200 connexions et vous avez un brasier RAM. Voilà pourquoi « augmentez max_connections » est souvent le clic le plus coûteux que vous pouvez faire.

Le disque est l’autre axe : le stockage des petits VPS fait souvent de la « mise en scène I/O »

Le disque du VPS peut être attaché au réseau, avec rafales, limité, ou sur stockage sur‑engagé. Les bases exposent brutalement ces mensonges de stockage. Si vous réglez la mémoire mais ignorez les modèles d’E/S (fsync, flush des pages sales), vous obtiendrez les symptômes classiques :

  • rapide jusqu’au jour où ça ne l’est plus
  • pics de latence aléatoires
  • blocages durant les checkpoints
  • retards de réplication apparents « sans raison »

Une citation fiabilité (idée paraphrasée)

« L’espoir n’est pas une stratégie. » — Général H. Norman Schwarzkopf (idée paraphrasée souvent utilisée en ingénierie et opérations)

Elle s’applique ici : vous ne pouvez pas espérer que le VPS n’atteindra pas la concurrence maximale ; vous devez poser des limites pour que le pic ne vous tue pas.

Profils de réglage sûrs (copier/coller) pour 2 Go

Ces profils visent d’abord à « ne pas planter », ensuite à « être assez rapide ». Ils supposent InnoDB (vous devriez être sur InnoDB sauf raison spécifique).

Avant de copier/coller : décidez du type de déploiement

  • VPS dédié BD : la base peut utiliser la majeure partie de la mémoire, mais laissez quand même de la marge pour l’OS.
  • Appli + BD sur le même VPS : la base doit rester modeste, et les limites de connexion strictes.

Décidez aussi des caractéristiques du stockage :

  • SSD‑like avec fsync correct : vous pouvez être plus agressif sur le flush.
  • Stockage réseau/pas cher : attendez‑vous à des douleurs fsync ; réglez pour éviter les blocages en rafales.

Profil A : « Appli + BD ensemble » (stack VPS bon marché, le plus courant)

Objectif : MySQL 8.0 ou MariaDB 10.6+ sur un VPS 2 Go qui exécute aussi un serveur d’application. C’est le profil à utiliser quand vous préférez dégrader la performance plutôt que de faire planter toute l’équipe.

cr0x@server:~$ sudo tee /etc/mysql/conf.d/99-vps-2gb-safe.cnf >/dev/null <<'EOF'
[mysqld]
# Core safety limits
max_connections                 = 60
skip_name_resolve               = ON
wait_timeout                    = 60
interactive_timeout             = 300

# InnoDB: keep it small and stable
innodb_buffer_pool_size         = 512M
innodb_buffer_pool_instances    = 1
innodb_log_buffer_size          = 16M
innodb_redo_log_capacity        = 256M
innodb_flush_method             = O_DIRECT
innodb_flush_log_at_trx_commit  = 1
innodb_io_capacity              = 200
innodb_io_capacity_max          = 400

# Reduce per-connection memory blowups
tmp_table_size                  = 32M
max_heap_table_size             = 32M
sort_buffer_size                = 2M
join_buffer_size                = 2M
read_buffer_size                = 1M
read_rnd_buffer_size            = 1M
thread_stack                    = 256K

# Keep table cache reasonable
table_open_cache                = 800
open_files_limit                = 65535

# Observability that pays rent
slow_query_log                  = ON
slow_query_log_file             = /var/log/mysql/slow.log
long_query_time                 = 1
log_queries_not_using_indexes   = OFF
EOF

Pourquoi ça marche : un buffer pool à 512 Mo ne battra pas des records, mais n’affamera pas votre appli. Des timeouts serrés réduisent le hoarding de connexions inactives. Des tmp/sort/join buffers modestes limitent les explosions mémoire par connexion. Vous choisissez un dommage borné.

Profil B : « VPS BD seul 2 Go » (plus de marge, mais pas un jouet)

Si la machine exécute uniquement la base (plus le monitoring), vous pouvez allouer plus à InnoDB.

cr0x@server:~$ sudo tee /etc/mysql/conf.d/99-vps-2gb-dbonly.cnf >/dev/null <<'EOF'
[mysqld]
max_connections                 = 120
skip_name_resolve               = ON
wait_timeout                    = 120

innodb_buffer_pool_size         = 1G
innodb_buffer_pool_instances    = 1
innodb_log_buffer_size          = 32M
innodb_redo_log_capacity        = 512M
innodb_flush_method             = O_DIRECT
innodb_flush_log_at_trx_commit  = 1
innodb_io_capacity              = 300
innodb_io_capacity_max          = 600

tmp_table_size                  = 64M
max_heap_table_size             = 64M
sort_buffer_size                = 2M
join_buffer_size                = 2M
read_buffer_size                = 1M
read_rnd_buffer_size            = 1M
thread_stack                    = 256K

table_open_cache                = 1200
open_files_limit                = 65535

slow_query_log                  = ON
slow_query_log_file             = /var/log/mysql/slow.log
long_query_time                 = 0.5
EOF

Règle stricte : ne mettez pas un buffer pool à 1,4 Go sur un VPS 2 Go puis ne soyez pas surpris s’il swappe. Vous serez sur le fil, et c’est là que résident les incidents.

Paramètres spécifiques à MariaDB à considérer sur les petites machines

MariaDB offre souvent des leviers opérationnels supplémentaires. Utilisez‑les avec prudence ; « plus de boutons » n’est pas automatiquement « mieux ».

  • Thread pool peut réduire le thrash des threads sous de fortes connexions, améliorant la latence en queue. Si vous l’activez, gardez un max_connections réaliste.
  • Aria et des moteurs hérités existent ; ne les utilisez pas accidentellement pour des tables à fort trafic d’écriture à moins de connaître leurs modes de panne.

Réalités spécifiques à MySQL sur les petites machines

  • Les valeurs par défaut de MySQL 8 sont généralement sensées, mais « sensées » suppose que vous ne lancez pas 200 connexions sur 2 Go.
  • Ne poursuivez pas des fonctionnalités supprimées comme le query cache. Si un guide vous dit de le tunner sur MySQL 8, fermez l’onglet.

À propos des réglages de durabilité (l’argument fsync)

innodb_flush_log_at_trx_commit=1 est la valeur durable par défaut. Sur un stockage médiocre, cela peut être pénible. Mettre 2 réduit la fréquence des fsync et améliore le débit, mais vous acceptez de perdre jusqu’à ~1 seconde de transactions en cas de crash. Si vous êtes sur un VPS unique sans réplication et que la conservation des données compte, n’« optimisez » pas la durabilité parce qu’un blog vous l’a conseillé. Si votre charge peut le tolérer (sessions, caches, données dérivées), c’est une décision métier. Notez‑la par écrit.

Blague n°2 : Désactiver la durabilité pour « réparer la performance » revient à enlever le détecteur de fumée parce que le bip vous énerve.

Tâches pratiques : commandes, sorties et décisions

Ces tâches sont conçues pour le moment exact où vous regardez un VPS 2 Go et vous demandez pourquoi il est lent, bloqué ou redémarre. Chaque tâche inclut : une commande, à quoi ressemble une sortie réaliste, ce que cela signifie, et la décision que vous prenez.

Tâche 1 : Confirmer ce que vous avez réellement installé (et éviter d’ajuster le mauvais démon)

cr0x@server:~$ mysql --version
mysql  Ver 8.0.36-0ubuntu0.22.04.1 for Linux on x86_64 ((Ubuntu))

Sens : La version vous indique quelles fonctionnalités existent et quels mythes de tuning ignorer. MySQL 8 n’a pas de query cache ; MariaDB a des valeurs par défaut différentes dans plusieurs domaines.

Décision : Associez vos conseils de tuning à votre version. Si vous êtes sur MariaDB, vérifiez aussi la version mineure exacte car les valeurs par défaut évoluent.

Tâche 2 : Vérifier la saveur du serveur depuis SQL

cr0x@server:~$ mysql -e "SELECT VERSION(), @@version_comment;"
+-----------+------------------------------+
| VERSION() | @@version_comment            |
+-----------+------------------------------+
| 8.0.36    | (Ubuntu)                     |
+-----------+------------------------------+

Sens : Cela confirme que vous êtes connecté au serveur que vous pensez. Ça paraît évident. Ça le devient moins pendant les migrations, basculements et containers de debug « temporaires ».

Décision : Si version_comment ne correspond pas à vos attentes, arrêtez‑vous et localisez le bon endpoint. Tweaker la mauvaise instance est une perte de journée particulière.

Tâche 3 : Vérifier RAM, swap et si vous vivez déjà dangereusement

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:           1.9Gi       1.2Gi       140Mi        42Mi       620Mi       420Mi
Swap:          1.0Gi       180Mi       820Mi

Sens : Peu de mémoire « disponible » et utilisation du swap signifient qu’il y a une vraie pression. Le swap sur un hôte BD corrèle souvent avec des pics de latence et des blocages.

Décision : Si le swap n’est pas nul et que la latence DB est mauvaise, réduisez l’empreinte mémoire de la BD (buffer pool, nombre de connexions, buffers par connexion) avant de « optimiser les requêtes ».

Tâche 4 : Prouver la participation de l’OOM killer (ou l’écarter)

cr0x@server:~$ sudo dmesg -T | tail -n 20
[Mon Dec 30 09:11:02 2025] Out of memory: Killed process 1421 (mysqld) total-vm:3074824kB, anon-rss:1320440kB, file-rss:0kB, shmem-rss:0kB, UID:110 pgtables:3140kB oom_score_adj:0
[Mon Dec 30 09:11:02 2025] oom_reaper: reaped process 1421 (mysqld), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

Sens : Ce n’est pas « MySQL a planté ». C’est « Linux l’a tué ». La solution est la capacité ou des limites mémoire, pas la réindexation.

Décision : Fixez des bornes strictes : baissez max_connections, réduisez les buffers par connexion et redimensionnez innodb_buffer_pool_size. Envisagez d’ajouter du swap seulement comme coussin de dernier recours, pas comme plan.

Tâche 5 : Identifier qui hoarde la mémoire maintenant

cr0x@server:~$ ps -eo pid,comm,rss,pmem --sort=-rss | head
  PID COMMAND           RSS %MEM
 1421 mysqld        1184500 60.2
  911 php-fpm8.1     210300 10.7
  947 php-fpm8.1     185120  9.4
  612 node            121800  6.1

Sens : Le RSS montre la mémoire résidente actuelle. C’est un instantané, mais ça indique si la BD est vraiment la source de pression.

Décision : Si mysqld est de loin le plus gros, ajustez la mémoire de la BD. Si l’appli prend plus, corrigez le modèle de processus de l’appli (nombre de workers, fuite, cache).

Tâche 6 : Confirmer la taille du buffer pool InnoDB et les variables mémoire clés

cr0x@server:~$ mysql -e "SHOW VARIABLES WHERE Variable_name IN ('innodb_buffer_pool_size','max_connections','tmp_table_size','max_heap_table_size','sort_buffer_size','join_buffer_size');"
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 536870912 |
| join_buffer_size        | 2097152   |
| max_connections         | 60        |
| max_heap_table_size     | 33554432  |
| sort_buffer_size        | 2097152   |
| tmp_table_size          | 33554432  |
+-------------------------+-----------+

Sens : Les valeurs sont en octets pour plusieurs variables. Le buffer pool à 512 Mo correspond au Profil A.

Décision : Si ces valeurs ne correspondent pas à votre profil voulu, vous dépannez de la fiction. Corrigez l’ordre de chargement de la config, supprimez les fragments conflictuels, redémarrez proprement.

Tâche 7 : Estimer la concurrence réelle des connexions et si max_connections est fantaisiste

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Threads_connected'; SHOW GLOBAL STATUS LIKE 'Max_used_connections';"
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_connected | 18    |
+-------------------+-------+
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| Max_used_connections| 54    |
+---------------------+-------+

Sens : Vous avez atteint 54 connexions concurrentes historiquement. Avec max_connections=60, vous êtes proche du plafond.

Décision : Si max_used est près du cap, soit (a) augmentez légèrement et réduisez les buffers par connexion, soit (b) corrigez le pooling dans l’appli pour éviter d’avoir réellement 60 connexions.

Tâche 8 : Vérifier les débordements de tables temporaires (un tueur de perf silencieux classique)

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Created_tmp%tables';"
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 1842  |
| Created_tmp_files       | 211   |
| Created_tmp_tables      | 9321  |
+-------------------------+-------+

Sens : Des tables temporaires sur disque ont lieu. Un peu c’est normal ; beaucoup indique des sorts/group-bys qui ne tiennent pas en mémoire ou des indexes manquants.

Décision : Si les tables temporaires disque sont élevées par rapport aux temporaires totales, ne relevez pas seulement tmp_table_size sur 2 Go. Corrigez la requête, ajoutez des index, ou acceptez les déversements disque et réglez l’E/S à la place.

Tâche 9 : Vérifier si le buffer pool est trop petit (ou juste correct)

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

Sens : Reads vs read_requests vous donne une idée des ratés de cache. Quelques ratés sont attendus. Un très fort ratio de ratés suggère un buffer pool trop petit ou une charge qui ne se met pas en cache.

Décision : Si les ratés sont énormes et que vous avez de la marge mémoire (et que vous ne swappez pas), augmentez le buffer pool prudemment. Si vous êtes limité en mémoire, corrigez les requêtes et les index plutôt.

Tâche 10 : Trouver rapidement les classes d’attente principales (snapshot du statut InnoDB)

cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | sed -n '1,120p'
*************************** 1. row ***************************
  Type: InnoDB
  Name:
Status:
=====================================
2025-12-30 09:22:11 0x7f2d2c1ff700 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 6 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 2199 srv_active, 0 srv_shutdown, 12195 srv_idle
srv_master_thread log flush and writes: 14394
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 31212
OS WAIT ARRAY INFO: signal count 30009
RW-shared spins 0, rounds 0, OS waits 12
RW-excl spins 0, rounds 0, OS waits 48
------------
TRANSACTIONS
------------
Trx id counter 1149091
History list length 188

Sens : C’est un check rapide de réalité. De forts OS waits dans les sémaphores peuvent indiquer de la contention ; une longue history list suggère du retard de purge (souvent causé par des transactions longues).

Décision : Si la history list grossit et reste élevée, traquez les transactions longues. Si les waits de sémaphore montent sous charge, réduisez la concurrence, optimisez les requêtes, et envisagez le thread pooling (surtout sur MariaDB).

Tâche 11 : Repérer les transactions longues qui gardent undo/purge occupés

cr0x@server:~$ mysql -e "SELECT trx_id, trx_started, trx_mysql_thread_id, trx_query FROM information_schema.innodb_trx ORDER BY trx_started;"
+--------+---------------------+---------------------+----------------------------------+
| trx_id | trx_started         | trx_mysql_thread_id | trx_query                        |
+--------+---------------------+---------------------+----------------------------------+
| 1148802| 2025-12-30 09:04:01 | 312                 | SELECT * FROM orders WHERE ...   |
+--------+---------------------+---------------------+----------------------------------+

Sens : Une transaction en cours depuis 09:04 peut maintenir des segments undo chauds et ralentir la purge, impactant les écritures et causant du bloat.

Décision : Corrigez le comportement applicatif (évitez les transactions interactives), ajoutez des index, et posez des timeouts raisonnables. Parfois la tuer est nécessaire, mais la prévention coûte moins cher.

Tâche 12 : Vérifier que le slow query log est activé et utile

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'slow_query_log'; SHOW VARIABLES LIKE 'long_query_time';"
+----------------+-------+
| Variable_name  | Value |
+----------------+-------+
| slow_query_log | ON    |
+----------------+-------+
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| long_query_time | 1.000 |
+-----------------+-------+

Sens : Les slow logs sont votre profileur à petit budget. Sur un VPS 2 Go, vous n’avez pas le loisir de deviner.

Décision : Si le slow log est désactivé, activez‑le. S’il est trop bruyant, augmentez temporairement long_query_time — ne désactivez pas le log.

Tâche 13 : Lire le slow log comme un opérateur, pas un touriste

cr0x@server:~$ sudo tail -n 25 /var/log/mysql/slow.log
# Time: 2025-12-30T09:27:14.123456Z
# User@Host: app[app] @ 10.0.0.15 []
# Query_time: 3.214  Lock_time: 0.002 Rows_sent: 50  Rows_examined: 982341
SET timestamp=1735550834;
SELECT * FROM events WHERE user_id=123 ORDER BY created_at DESC LIMIT 50;

Sens : Rows_examined ~ 1M pour un LIMIT 50 est un classique d’index manquant/pauvre. Lock_time est minime ; ce n’est pas du « locking », c’est du « travail ».

Décision : Ajoutez/ajustez un index (probablement (user_id, created_at)), ou réécrivez la requête pour éviter le scan. Ne touchez pas aux tailles de buffer pour « réparer » ça.

Tâche 14 : Confirmer que vous n’êtes pas accidentellement en double cache (mauvais avec des mismatches O_DIRECT)

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'innodb_flush_method';"
+---------------------+----------+
| Variable_name       | Value    |
+---------------------+----------+
| innodb_flush_method | O_DIRECT |
+---------------------+----------+

Sens : O_DIRECT réduit le double buffering entre le buffer pool InnoDB et le cache de page de l’OS. Sur une petite RAM, cela aide à garder la mémoire prévisible.

Décision : Si ce n’est pas O_DIRECT et que vous êtes sous pression mémoire, envisagez de basculer (testez d’abord ; certains stacks de stockage se comportent étrangement).

Tâche 15 : Inspecter l’ouverture des tables et la pression sur les descripteurs de fichiers

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Open_tables'; SHOW VARIABLES LIKE 'table_open_cache';"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_tables   | 790   |
+---------------+-------+
+------------------+------+
| Variable_name    | Value|
+------------------+------+
| table_open_cache | 800  |
+------------------+------+

Sens : Si Open_tables atteint régulièrement la limite du cache, MySQL ouvrira/fermera des tables fréquemment, causant overhead et contention metadata.

Décision : Si vous avez de la marge en FD et mémoire, augmentez modérément table_open_cache. Sur 2 Go, ne le montez pas à 10 000 comme s’il était gratuit.

Tâche 16 : Vérifier les symptômes de latence I/O côté OS

cr0x@server:~$ iostat -xz 1 3
Linux 5.15.0-91-generic (server) 	12/30/2025 	_x86_64_	(2 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          18.21    0.00    6.44   22.12    0.00   53.23

Device            r/s     w/s   rkB/s   wkB/s  await  %util
vda              42.0    58.0   912.0  2310.0  48.30  92.10

Sens : await ~48ms et %util ~92 % indiquent que le disque est le goulot. Un iowait élevé confirme cela.

Décision : Si le disque est saturé, arrêtez de tuner les paramètres CPU. Réduisez l’amplification d’écriture (indexes, taille des transactions), réglez la capacité redo sensiblement, et envisagez d’améliorer le stockage ou de déplacer la BD hors de la machine.

Playbook de diagnostic rapide

Ceci est le flux « vous avez 15 minutes pour avoir l’air compétent ». Il est ordonné pour trouver rapidement le goulot sur un VPS 2 Go.

Première étape : est‑ce que ça meurt (OOM, redémarrages ou swap) ?

  • Vérifiez dmesg pour les kills OOM.
  • Vérifiez free -h pour l’activité swap et la faible mémoire disponible.
  • Vérifiez si mysqld redémarre (logs systemd).
cr0x@server:~$ sudo journalctl -u mysql --since "1 hour ago" | tail -n 30
Dec 30 09:11:04 server systemd[1]: mysql.service: Main process exited, code=killed, status=9/KILL
Dec 30 09:11:04 server systemd[1]: mysql.service: Failed with result 'signal'.
Dec 30 09:11:06 server systemd[1]: mysql.service: Scheduled restart job, restart counter is at 3.

Décision : S’il y a OOM/redémarrages, priorisez les limites mémoire et les caps de connexion avant toute micro‑optimisation.

Deuxième étape : est‑ce disque (iowait, stalls fsync, débordements temp) ?

  • iostat -xz pour await/%util.
  • Vérifiez les débordements de tables temporaires disque.
  • Vérifiez la pression sur les redo/log flush.
cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_os_log_fsyncs'; SHOW GLOBAL STATUS LIKE 'Innodb_os_log_written';"
+----------------------+--------+
| Variable_name        | Value  |
+----------------------+--------+
| Innodb_os_log_fsyncs | 192113 |
+----------------------+--------+
+-----------------------+-----------+
| Variable_name         | Value     |
+-----------------------+-----------+
| Innodb_os_log_written | 987654321 |
+-----------------------+-----------+

Décision : Si le disque est le goulot, concentrez‑vous sur la correction des requêtes/index et la réduction des écritures. Ne gonflez pas le buffer pool d’une façon qui force le swap ; vous échangerez une panne contre une autre.

Troisième étape : est‑ce CPU/locking (trop de threads, contention) ?

  • top / pidstat pour voir la saturation CPU.
  • Statut InnoDB pour les waits de sémaphores et la contention sur les verrous de lignes.
  • Processlist pour de nombreuses requêtes lourdes simultanées.
cr0x@server:~$ mysql -e "SHOW FULL PROCESSLIST;" | head -n 15
Id	User	Host	db	Command	Time	State	Info
311	app	10.0.0.15:41012	prod	Query	12	Sending data	SELECT * FROM events WHERE user_id=...
312	app	10.0.0.15:41014	prod	Query	520	Updating	UPDATE inventory SET ...
313	app	10.0.0.15:41015	prod	Sleep	58		NULL

Décision : Si vous voyez de longues mises à jour et beaucoup de lecteurs bloqués sur « Sending data », vous avez probablement un problème d’indexation ou de conception des transactions, pas un problème magique de configuration.

Trois mini-récits d’entreprise (parce que l’échec est un professeur)

1) Incident causé par une mauvaise hypothèse : « max_connections est juste un cap, pas un réglage mémoire »

Ils faisaient tourner un petit portail client sur un VPS 2 Go : web app, base, tout sur la même machine. Pendant un pic saisonnier, le portail n’a pas seulement ralenti. Il a commencé à redémarrer. L’équipe a supposé un mauvais noyau ou un « voisin bruyant » sur l’hôte VPS.

La première correction qu’ils ont tentée était classique : augmenter max_connections de 100 à 400 parce que les clients recevaient des « trop de connexions ». L’incident s’est empiré. Le site ne faisait plus seulement des erreurs ; il est entré dans une boucle de redémarrages de la base et de déploiements à moitié terminés.

En post‑mortem, nous avons revu la comptabilité mémoire. Chaque connexion pouvait allouer plusieurs buffers. Lors d’un pic, les connexions se propageaient, certaines effectuant des sorts et des jointures, chacune grappillant des mégaoctets. Multipliez par des centaines et vous avez une falaise mémoire. Le noyau a fait ce qu’il fait : il a tué mysqld.

La correction ennuyeuse mais correcte était : limiter strictement les connexions, faire appliquer le pooling applicatif, réduire les buffers par connexion et garder le buffer pool InnoDB modeste. Puis, parce que les humains oublient, ajouter des alertes sur Max_used_connections et l’usage du swap.

Le résultat n’était pas « plus rapide » au sens benchmark. Il était stable. Le portail se dégradait gracieusement sous les pics au lieu d’exploser. En production, la dégradation contrôlée est une fonctionnalité.

2) Optimisation qui s’est retournée contre eux : « Augmentons tmp_table_size pour éviter les déversements disque »

Un service orienté reporting souffrait de groupes/aggregations lents. Quelqu’un a vu beaucoup de Created_tmp_disk_tables et a décidé de monter tmp_table_size et max_heap_table_size de manière drastique. Sur le papier, moins de tables temporaires disque = moins d’E/S = requêtes plus rapides. C’est séduisant.

Le changement a fonctionné en test. Puis production l’a reçu. L’utilisation mémoire a grimpé sous reporting concurrent. Pas linéairement. Soudainement. Le service ne s’est pas juste ralenti ; il est tombé. L’OOM killer a rejoint les réunions quotidiennes.

Le subtil : les tables temporaires en mémoire consomment de la mémoire par session, et des requêtes complexes peuvent créer de grandes structures temporaires. Sous concurrence, cette « aide » devient une responsabilité non bornée. Sur un VPS 2 Go, non borné veut dire « bientôt ».

La correction correcte a été multiple et légèrement pénible : ajouter des index pour réduire le besoin de tables temporaires, réécrire les rapports les plus lourds, et accepter que certains déversements disque soient normaux. Garder tmp_table_size modeste pour que le mode de panne soit « rapport lent », pas « base morte ».

Ils ont aussi déplacé les charges de reporting hors pic et introduit une replica plus tard. Le vrai gain n’était pas le bouton réglé. C’était d’admettre que la charge n’appartenait pas au primaire pendant les heures de production.

3) Pratique ennuyeuse mais correcte qui a sauvé la mise : « slow query log + un index par incident »

Une autre équipe faisait tourner un petit SaaS sur MariaDB avec un budget strict de 2 Go. Rien de magique. Ils avaient une pratique presque trop basique : le slow query logging était toujours activé, tourné et revu après chaque incident.

Quand une nouvelle fonctionnalité a été lancée, la latence p95 a doublé. Ils n’ont pas commencé par changer le buffer pool ou le flush des logs. Ils ont pris le slow log et trouvé une requête qui examinait trop de lignes pour un simple tableau de bord utilisateur. Ce n’était pas malveillant. Il manquait juste le bon index composite.

Ils ont ajouté l’index, déployé, et la latence est revenue à la normale. Pas de changement effrayant de configuration. Pas de compromis de durabilité « temporaire ». Pas de projet perf d’une semaine. Juste des boucles de rétroaction disciplinées.

Plus tard, quand ils ont affiné la config, c’était guidé par des goulots observés — pics de connexions, ratios de temp table, attente disque. Le secret de l’équipe n’était pas le génie. C’était de refuser d’opérer à l’aveugle.

Si vous tournez sur du petit matériel, l’observabilité ennuyeuse n’est pas une surcharge. C’est le loyer.

Erreurs courantes : symptôme → cause racine → correction

1) Symptom : mysqld se fait tuer, redémarre, ou disparaît

Cause racine : OOM killer dû à une mémoire par-connexion non bornée, un buffer pool surdimensionné, ou une compétition RAM entre appli et BD.

Correction : Réduire max_connections, réduire les buffers par connexion (sort/join/read), redimensionner correctement innodb_buffer_pool_size, et assurer que l’appli utilise du pooling. Confirmer avec dmesg.

2) Symptom : erreurs « Trop de connexions » pendant les pics

Cause racine : churn de connexions et absence de pooling ; ou max_connections trop bas pour la pointe de charge.

Correction : Mettre en place du pooling (au niveau appli ou proxy), raccourcir wait_timeout pour libérer les connexions inactives, et augmenter max_connections seulement si vous limitez aussi la mémoire par connexion.

3) Symptom : pics de latence aléatoires, surtout sur les écritures

Cause racine : stalls disque (fsync, checkpointing, stockage bon marché), ou flushs de fond qui se battent avec les requêtes au premier plan.

Correction : Vérifier avec iostat. Régler la capacité redo modérément, garder la durabilité sauf si vous acceptez explicitement la perte de données, et réduire l’amplification d’écriture (indexes, taille des lots).

4) Symptom : CPU élevé, load average qui monte, mais les requêtes ne sont pas « si grosses »

Cause racine : trop de threads exécutables à cause d’une concurrence excessive ; requêtes inefficaces ; indexes manquants causant des scans.

Correction : Baisser la concurrence (connexions), utiliser le slow log, ajouter des indexes, et envisager le thread pool (surtout sur MariaDB) pour réduire le thrash du scheduler.

5) Symptom : réplication en retard (si vous avez une replica)

Cause racine : l’I/O de la replica ne suit pas (disque), ou le thread SQL est bloqué par des transactions longues ou des requêtes lourdes.

Correction : Vérifier l’état de la réplication, réduire la charge d’écriture, et optimiser les requêtes les plus lentes. Sur 2 Go, faire tourner réplication + appli + BD est ambitieux ; soyez honnête sur les ressources.

6) Symptom : les tables temporaires remplissent « mystérieusement » le disque

Cause racine : grands sorts/group-bys déversés sur disque ; tmpdir sur un petit système de fichiers ; indexes insuffisants.

Correction : Mettre tmpdir sur un filesystem avec de l’espace, mais surtout corriger les requêtes/indexes. Ne montez pas juste tmp_table_size sur 2 Go.

7) Symptom : l’état « Sending data » domine la processlist

Cause racine : scans de table/index et évaluation lourde de lignes ; parfois un mauvais ordre de jointure.

Correction : Utiliser slow log + EXPLAIN, ajouter les bons indexes, réduire les colonnes sélectionnées, et paginer correctement. La config ne vous sauvera pas d’un scan de millions de lignes pour le plaisir.

Checklists / plan étape par étape

Étape par étape : stabiliser d’abord (éviter les plantages), puis optimiser

  1. Confirmer la version et le moteur : MySQL vs MariaDB, version exacte, et que les tables sont InnoDB.
  2. Poser des bornes de connexion : choisir un max_connections réaliste (60–120 sur 2 Go selon l’appli et le coût par requête).
  3. Caper la mémoire par connexion : garder les sort/join buffers modestes ; n’« aidez » pas chaque requête avec des buffers énormes.
  4. Redimensionner le buffer pool : 512M pour appli+BD, ~1G pour BD seule comme point de départ.
  5. Activer le slow query logging : toujours, avec rotation des logs en place.
  6. Vérifier que vous ne swappez pas : le swap n’est pas un cache BD, c’est une taxe de performance.
  7. Mesurer la latence disque : utiliser iostat ; si l’await est mauvais, acceptez que vous êtes lié au stockage.
  8. Corriger les 3 requêtes principales : ajouter des index, réécrire, réduire les scans. Ne commencez pas par changer 40 réglages.
  9. Mettre des alertes : usage swap, Max_used_connections proche du cap, util disque, et volume de slow queries.
  10. Retester sous charge : test de charge en staging ou montée contrôlée en production. Surveillez mémoire et disque.

Limites de bon sens pour un VPS 2 Go (règles empiriques)

  • max_connections : 40–80 si appli+BD ; 80–150 si BD seule et charge légère par connexion.
  • innodb_buffer_pool_size : 512M (partagé) à 1G (BD seule). Rarement plus.
  • tmp_table_size / max_heap_table_size : 32M–64M. Plus grand est un piège sous concurrence.
  • sort/join buffers : 1M–4M max dans la plupart des cas sur faible RAM. Les grandes valeurs sont pour des workloads spécialisés avec faible concurrence.
  • Durabilité : gardez innodb_flush_log_at_trx_commit=1 sauf si vous avez une tolérance explicite à la perte de données.

Hygiène opérationnelle qui évite la « lenteur mystérieuse »

  • Faire tourner les slow logs et les error logs pour éviter de remplir le disque.
  • Garder les tables et indexes propres ; éviter les indexes inutilisés qui amplifient les écritures.
  • Exécuter ANALYZE TABLE quand les plans de requête deviennent bizarres après de gros changements de données (avec parcimonie, hors‑pic).
  • Planifier les montées de version ; sauter une grosse version en dernière minute sur un VPS 2 Go est la meilleure façon de rencontrer les pires parties de l’optimiseur.

FAQ

1) MariaDB est‑elle plus rapide que MySQL sur un VPS 2 Go ?

Parfois, pour des charges spécifiques, mais le facteur dominant sur 2 Go est généralement la pression mémoire et la latence disque. Choisissez le moteur que vous pouvez exploiter proprement, puis corrigez les requêtes.

2) Quel est le réglage le plus important pour éviter les plantages ?

max_connections, parce qu’il bride indirectement l’explosion mémoire par connexion. Juste derrière : garder les buffers par connexion modestes.

3) Quelle taille pour innodb_buffer_pool_size sur 2 Go ?

Commencez à 512M si la machine héberge aussi l’appli. Si c’est BD seule, commencez autour de 1G. Augmentez seulement si vous ne swappez pas et que les ratés de cache nuisent vraiment à la perf.

4) Dois‑je activer le swap sur un VPS base de données ?

Le swap peut éviter une mort OOM immédiate, mais il peut aussi provoquer de longs pics de latence. Si vous utilisez du swap, traitez‑le comme un coussin d’urgence et maintenez la mémoire BD conservatrice. Si le swap devient courant, vous êtes sous‑dimensionné ou mal configuré.

5) innodb_flush_log_at_trx_commit=2 est‑il acceptable ?

Cela n’est acceptable que si le métier accepte de perdre environ une seconde de transactions en cas de crash. Sur un VPS unique tenant les données primaires, la durabilité par défaut est généralement le bon choix.

6) Pourquoi ne pas gonfler tmp_table_size pour éviter les temp tables disque ?

Parce que sur une petite machine, c’est une grenade sous concurrence. Quelques requêtes concurrentes peuvent consommer chacune des centaines de Mo dans le pire des cas. Corrigez la requête et les index d’abord.

7) Dois‑je faire tourner l’appli et la base ensemble sur 2 Go ?

Vous pouvez, mais il faut être strict : limiter la mémoire BD, caper les connexions, et réduire le nombre de workers de l’appli. Si l’appli grossit, séparez‑les tôt — le scaling vertical sur une petite machine a des bords tranchants.

8) Quelle est la façon la plus rapide de dire si je suis CPU‑bound ou disque‑bound ?

Utilisez iostat -xz. Un %iowait élevé, un await élevé et un %util élevé pointent vers le disque. Si l’iowait est faible mais les CPUs saturés, vous êtes lié au CPU/requêtes/concurrence.

9) Dois‑je régler table_open_cache sur 2 Go ?

Seulement si vous observez du churn (Open_tables proche de la limite du cache et problèmes de performance liés aux opérations metadata). Gardez‑le modéré ; de grands caches consomment mémoire et descripteurs fichiers.

10) Si je ne peux corriger qu’une chose dans l’application, laquelle ?

Le pooling de connexions et des timeouts raisonnables. Ça réduit le besoin d’un max_connections élevé et diminue fortement la volatilité mémoire.

Conclusion : prochaines étapes pragmatiques

Si vous voulez un VPS 2 Go avec une base qui ne plante pas, cessez de penser en « meilleure performance » et commencez à penser en « défaillance bornée ». Cappez les connexions. Gardez les buffers par connexion petits. Donnez à InnoDB un buffer pool adapté à la réalité, pas à l’ego. Ensuite utilisez le slow query log pour gagner des améliorations réelles de performance de la seule manière honnête : en faisant moins de travail.

Faites ceci ensuite, dans l’ordre

  1. Choisissez le Profil A (partagé) ou le Profil B (BD seule) et appliquez‑le proprement ; redémarrez le service.
  2. Exécutez les vérifications de diagnostic rapide : free, dmesg, iostat, Threads_connected/Max_used_connections.
  3. Consultez le slow log pour les plus gros coupables ; corrigez la pire requête avec un index ou une réécriture.
  4. Posez des alertes pour l’usage du swap et Max_used_connections approchant votre cap.
  5. Si vous êtes toujours limité : séparez l’appli et la BD, ou augmentez la RAM/le stockage. Sur 2 Go, « scaler » verticalement est souvent l’optimisation la moins chère que vous puissiez acheter.

MySQL et MariaDB peuvent tous deux bien se comporter sur un VPS 2 Go. L’astuce n’est pas de trouver les réglages parfaits. C’est de refuser qu’un seul réglage rende la défaillance non bornée.

← Précédent
Ajustement des paramètres du noyau Ubuntu 24.04 : les 5 sysctls qui comptent (et les 10 qui ne servent pas) (cas n°42)
Suivant →
MariaDB vs RDS MariaDB : Qui réserve le moins de surprises de compatibilité ?

Laisser un commentaire