Optimiser la base de données MySQL d'un serveur FiveM
Un serveur RP sollicite énormément sa base : positions, inventaires, argent, véhicules. Une base mal optimisée, c'est du lag et des hitchs. Voici comment diagnostiquer puis optimiser.
Un serveur FiveM RP sollicite constamment sa base de données. Positions, inventaires, comptes bancaires, véhicules, propriétés et états des personnages génèrent de nombreuses lectures et écritures. Lorsque MySQL ou MariaDB répond lentement, le problème se répercute sur tout le serveur : lag, hitchs, connexions longues et désynchronisations.
Sur ESX comme sur QBCore, oxmysql sert d’intermédiaire entre les ressources FiveM et la base. Il facilite les échanges, mais ne peut pas compenser des tables sans index, des requêtes mal conçues ou un stockage saturé. La bonne méthode consiste donc à mesurer, identifier la source des ralentissements, puis optimiser progressivement.
Reconnaître les symptômes d’une base lente
Une base de données surchargée ne provoque pas toujours un crash visible. Elle dégrade souvent les performances par à-coups. Les symptômes les plus fréquents sont :
- des hitchs au moment des sauvegardes automatiques ;
- un délai important pendant la connexion ou le chargement d’un personnage ;
- des inventaires, garages ou menus qui mettent plusieurs secondes à s’ouvrir ;
- des ralentissements lorsque le nombre de joueurs augmente ;
- des pertes temporaires de synchronisation ;
- des avertissements oxmysql concernant des requêtes lentes dans la console serveur ou la console F8.
Il faut toutefois éviter de conclure trop vite. Un hitch peut aussi venir d’une ressource Lua trop lourde, d’une boucle mal maîtrisée ou d’un manque de CPU. Notre guide pour optimiser un serveur avec resmon aide à isoler ces causes. Les mesures permettent de distinguer un problème SQL d’un problème purement FiveM.
Mesurer avant d’optimiser
Commencez par observer les requêtes lentes remontées par oxmysql. Le seuil d’avertissement peut être configuré dans le fichier de démarrage du serveur, par exemple :
set mysql_slow_query_warning 200
Avec cette valeur, oxmysql signale les requêtes dépassant 200 millisecondes. Le seuil doit être adapté à votre serveur. L’objectif n’est pas de supprimer immédiatement chaque avertissement, mais d’identifier les requêtes lentes, fréquentes ou exécutées en rafale.
Pour chaque entrée, relevez :
- la ressource à l’origine de la requête ;
- la table concernée ;
- la durée d’exécution ;
- la fréquence d’apparition ;
- le nombre de lignes examinées ou retournées.
Une requête de 300 millisecondes lancée une fois par jour est moins urgente qu’une requête de 40 millisecondes exécutée plusieurs centaines de fois par minute. Recherchez surtout les ressources qui répètent la même lecture pour chaque joueur ou dans un thread rapide.
Le slow query log de MySQL ou MariaDB peut compléter les journaux oxmysql. Sur une production active, utilisez les modes de débogage avec prudence afin de ne pas générer un volume excessif de logs.
Ajouter les bons index
Les index constituent généralement le levier le plus efficace. Sans index adapté, MySQL peut parcourir toute une table pour trouver quelques lignes. Ce scan complet devient coûteux dès que les tables de personnages, véhicules ou inventaires grossissent.
Les colonnes utilisées fréquemment dans les clauses WHERE, JOIN et parfois ORDER BY doivent être examinées en priorité. Dans un environnement FiveM, il s’agit souvent de identifier, license, owner ou plate.
Exemples :
CREATE INDEX idx_users_identifier
ON users(identifier);
CREATE INDEX idx_owned_vehicles_owner
ON owned_vehicles(owner);
CREATE INDEX idx_owned_vehicles_plate
ON owned_vehicles(plate);
Pour une requête qui filtre régulièrement sur plusieurs colonnes, un index composite peut être plus pertinent :
CREATE INDEX idx_vehicles_owner_stored
ON owned_vehicles(owner, stored);
L’ordre des colonnes compte. Cet index aide les recherches sur owner ou sur owner et stored, mais pas nécessairement celles portant uniquement sur stored.
Avant tout ajout, vérifiez les index existants et utilisez EXPLAIN sur la requête problématique. Ajouter aveuglément plusieurs index peut ralentir les insertions et mises à jour, augmenter la taille de la base et consommer davantage de mémoire. Un index doit répondre à un usage réel.
Corriger les requêtes des ressources
Une configuration MySQL parfaite ne sauvera pas une ressource qui interroge la base à chaque tick. Les accès SQL ne doivent jamais servir de système d’état temps réel.
Chargez les données nécessaires à la connexion du joueur, conservez les valeurs actives en mémoire, puis écrivez les changements à des moments contrôlés. Une position peut par exemple être mise à jour côté serveur en mémoire et sauvegardée périodiquement, au lieu de déclencher une requête à chaque mouvement.
Évitez également le problème N+1 : une première requête récupère une liste, puis une nouvelle requête est lancée pour chaque élément. Une jointure, une requête groupée ou un chargement par lot réduit fortement les allers-retours.
Les ressources mal conçues qui spamment MySQL sont souvent la cause réelle des hitchs. Désactivez temporairement une ressource suspecte sur un environnement de test et comparez les temps de réponse avant de modifier toute la configuration du serveur SQL.
Ajuster les sauvegardes de personnages
ESX et QBCore sauvegardent périodiquement les personnages. Sur un petit serveur, un intervalle court peut passer inaperçu. Sur un serveur accueillant beaucoup de joueurs, lancer simultanément des dizaines ou centaines de mises à jour peut provoquer un pic d’activité et un hitch.
Vérifiez l’intervalle configuré par votre framework et vos ressources. Une sauvegarde trop fréquente apporte peu de sécurité supplémentaire si les données changent rarement. À l’inverse, un intervalle excessivement long augmente les pertes possibles après un crash.
Lorsque le framework le permet, répartissez les sauvegardes dans le temps ou utilisez des opérations groupées. Sauvegardez immédiatement les événements critiques, comme un achat important ou une déconnexion, tout en conservant un cycle périodique raisonnable pour le reste.
Configurer MySQL ou MariaDB
Le paramètre innodb_buffer_pool_size contrôle la quantité de mémoire utilisée par InnoDB pour conserver les données et index fréquemment consultés. Un buffer pool suffisant évite de relire constamment les mêmes pages depuis le disque.
Exemple de configuration :
[mysqld]
innodb_buffer_pool_size=4G
Cette valeur n’est pas universelle. Elle dépend de la RAM disponible, de la taille de la base et des autres services présents sur la machine. Si FiveM et MySQL partagent le même serveur, il faut conserver assez de mémoire pour FXServer, les ressources et le système.
Surveillez aussi le nombre de connexions, la taille des journaux InnoDB, les tables temporaires et les délais d’attente. Ne copiez pas une configuration dite magique trouvée pour un serveur disposant de 64 Go de RAM sur une machine de 8 Go. Modifiez un paramètre à la fois, redémarrez si nécessaire, puis mesurez l’impact.
Entretenir les tables
Une base FiveM accumule rapidement des données inutiles : logs anciens, historiques d’actions, véhicules abandonnés, personnages inactifs ou entrées orphelines laissées par des ressources supprimées.
Définissez une politique de rétention et archivez les données qui doivent être conservées. Les suppressions doivent respecter les relations entre tables afin de ne pas casser les inventaires, propriétés ou véhicules associés.
Après un nettoyage important, certaines tables peuvent bénéficier d’une optimisation :
OPTIMIZE TABLE owned_vehicles;
Cette opération peut verrouiller ou réorganiser la table selon le moteur et la version utilisés. Exécutez-la pendant une période de faible activité, après sauvegarde, et non comme une tâche quotidienne automatique. Une maintenance régulière est utile, mais elle ne remplace pas de bons index et de bonnes requêtes.
Dimensionner le stockage et la machine
MySQL dépend fortement des entrées et sorties. Un stockage NVMe réduit les temps de lecture, d’écriture et de synchronisation des journaux par rapport à un disque lent. La RAM est tout aussi importante, car elle permet au buffer pool de conserver les données chaudes en mémoire.
Le processeur doit offrir de bonnes performances par coeur, tandis que des vCores dédiés limitent les variations causées par des voisins trop actifs. Les offres FiveM TalCloud démarrent à 5,49 € par mois et combinent NVMe, vCores dédiés, hébergement en France, Anti-DDoS et txAdmin préinstallé, avec un déploiement annoncé en 60 secondes. Ce socle facilite l’hébergement, mais le code et le schéma SQL doivent malgré tout rester optimisés.
Utiliser correctement oxmysql
Utilisez systématiquement des paramètres plutôt que de concaténer des valeurs dans les requêtes :
local vehicle = MySQL.single.await(
'SELECT plate, vehicle FROM owned_vehicles WHERE owner = ? AND plate = ?',
{ identifier, plate }
)
Les paramètres améliorent la sécurité et permettent au moteur de mieux réutiliser les requêtes préparées. Surveillez toutefois le nombre de prepared statements si des ressources génèrent continuellement des requêtes SQL différentes. Notre guide sur la base de données oxmysql rappelle les bases de cette configuration.
Évitez les longues chaînes d’appels synchrones ou séquentiels lorsqu’ils peuvent être regroupés. Les variantes await restent pratiques pour écrire un code lisible, mais elles ne justifient pas de bloquer la logique d’une ressource avec des dizaines d’accès successifs. Limitez les colonnes retournées et préférez SELECT plate, vehicle à SELECT * lorsque seules deux valeurs sont nécessaires.
Sauvegarder et valider chaque changement
Avant de supprimer des données, modifier un index ou ajuster une table, réalisez une sauvegarde :
mysqldump -u fivem -p --single-transaction fivem > fivem-backup.sql
Vérifiez que le fichier produit n’est pas vide et testez régulièrement sa restauration sur une base séparée. Notre guide pour sauvegarder un serveur FiveM détaille cette étape. Une sauvegarde non restaurable ne protège pas votre serveur.
Appliquez ensuite un seul changement à la fois. Comparez les temps de requête, les hitchs et la charge avant et après. Testez les migrations sur une copie de la base, planifiez les opérations lourdes hors des heures de pointe et conservez une procédure de retour arrière. L’optimisation durable repose sur des mesures, pas sur une accumulation de réglages aléatoires.