Von mysql-async zu oxmysql: Sichere Migration & Query-Muster
·von (Founder & Lead Editor)··6 Min. Lesezeit·
Schritt-für-Schritt-Tutorial zur sicheren Migration von mysql-async zu oxmysql – Queries beschleunigen und SQL-Nutzung modernisieren. Vollständiger Guide für 2026.
Share
Von mysql-async zu oxmysql: Sichere Migration & Query-Muster
Einleitung: Zielgruppe
Zielgruppe: FiveM-Server-Besitzer, Skripter, Maintainer
Ziel:mysql-async sicher durch oxmysql ersetzen, Queries beschleunigen und SQL-Nutzung modernisieren.
Dieser Guide ist Teil unseres , in dem wir ESX, QBCore und QBOX ausführlich vergleichen und dir bei der Wahl des richtigen Frameworks helfen.
Weitere Lektüre:
FiveM Server-Optimierung: Das definitive Playbook 2026 —
Adapter-Muster: ESX↔QBCore↔QBOX Exports, Events & Player Models —
Staging-Umgebung, die das Produktionsschema + Datensubset spiegelt.
Häufig gestellte Fragen
Wie migriere ich sicher von `mysql-async` zu `oxmysql`, falls unerwartete Probleme auftreten?
Der Artikel betont die Wichtigkeit eines Rollback-Plans. Konkret bedeutet das, dass du vor der Umstellung auf jeden Fall ein vollständiges Datenbank-Backup mit `mysqldump --single-transaction yourdb > backup.sql` erstellen solltest. Bewahre außerdem die alten Versionen deiner Skripte, die noch `mysql-async` verwenden, auf. Falls nach der Umstellung Probleme auftreten, kannst du so schnell zur vorherigen Konfiguration zurückkehren, indem du das Backup wiederherstellst und die älteren Skriptversionen aktivierst. Teste diesen Rollback-Prozess idealerweise vorher in deiner Staging-Umgebung, um im Ernstfall Zeit zu sparen.
Welche konkreten Änderungen muss ich in meinen Scripten vornehmen, um `oxmysql` anstelle von `mysql-async` zu nutzen?
Die Umstellung erfordert hauptsächlich Anpassungen in deinen SQL-Abfragen. Anstelle von `@param` für Parameter musst du entweder `?` für positionale Parameter oder `:name` für benannte Parameter verwenden. Außerdem musst du die `MySQL.Async.*`-Aufrufe durch `MySQL.*` oder `exports.oxmysql:*` ersetzen. Achte darauf, dass die `oxmysql`-API möglicherweise andere Rückgabewerte oder Fehlerbehandlungen hat, also überprüfe deine Skripte sorgfältig, besonders in Bezug auf Fehlerbehandlung und Datentypen.
Wie kann ich die Performance-Verbesserungen durch `oxmysql` nach der Migration auf meinem FiveM-Server überprüfen?
Der Artikel erwähnt ein Micro-Benchmark-Harness, mit dem du die Performance auf deiner spezifischen Hardware testen kannst. Dieses Harness ermöglicht es dir, verschiedene SQL-Abfragen unter realen Bedingungen zu simulieren und die Ausführungszeiten zu messen. Vergleiche die Ergebnisse vor und nach der Migration, um die tatsächlichen Performance-Gewinne durch `oxmysql` zu quantifizieren. Konzentriere dich dabei besonders auf Abfragen, die in deinen Skripten häufig verwendet werden oder bekanntermaßen langsam sind.
Von der Recherche zu einem produktionsreifen Server-Stack wechseln
Sobald die Richtung klar ist, kommst du über diese Angebotsseiten direkt zu verifizierten Scripts, kuratierten Bundles und framework-spezifischen Kaufpfaden.
Framework hub
Move into the QBCore landing page to compare verified scripts, framework fit, and install-ready products built for modern FiveM servers.
Open QBCore hub
Premium catalog
Move from research into the main shop to compare real products, framework labels, screenshots, and production-ready quality signals.
Open premium shop
Launch faster
Bundles shorten the path from planning to launch by grouping the highest-leverage scripts into a cleaner commercial starting point.
View bundles
Premium-Scripts, die dir gefallen könnten
Kostenlose Scripts die dich interessieren könnten
Ähnliche Artikel
Alles, was du über ox_lib wissen musst – die wichtigste Utility-Bibliothek im FiveM-Ökosystem. Behandelt Features, Installation, UI-Komponenten und das OX-Ökosystem.
Schritt-für-Schritt-Tutorial zur Bewertung von FiveM-Skripten vor dem Kauf – mit Antwortzeit unter 24h, Lizenzbedingungen und Performance-Benchmarks. Vollständiger Guide für 2026.
QBox hat sich 2026 fest als natuerlicher Nachfolger von QBCore im FiveM-Roleplay-Oekosystem etabliert.
```lua
```lua
set mysql_connection_string "mysql://user:pass@127.0.0.1:3306/yourdb?charset=utf8mb4"
# Optionale Diagnosen
```lua
```lua
set mysql_slow_query_warning 200 # Queries langsamer als 200ms protokollieren
set mysql_debug false # true für ausführliches Logging im Staging
mysql-async während der Staging-Phase deaktiviert aber im resources-Ordner verfügbar lassen (für schnellen Rollback).
Parameter: @param-Style-Tabellen wie { ['@identifier']=identifier }
oxmysql (modern):
Callback-Style via Export: exports.oxmysql:query|scalar|single|insert|update(sql, params, cb)
Promise/Await via Global: MySQL.query|scalar|single|insert|update.await(sql, params) und Nicht-Await-Callbacks ohne .await
Parameter: positional? via Array, oder benannt:name via Objekt
3.1 Häufige Ersetzungen
SELECT viele
```lua
```lua
-- mysql-async
MySQL.Async.fetchAll(
'SELECT * FROM users WHERE identifier = @id',
{ ['@id'] = identifier },
function(rows) ... end
)
-- oxmysql (Callback via Export)
exports.oxmysql:query(
'SELECT * FROM users WHERE identifier = ?',
{ identifier },
function(rows) ... end
)
-- oxmysql (await)
local rows = MySQL.query.await(
'SELECT * FROM users WHERE identifier = ?',
{ identifier }
)
SELECT einzelne Zeile
```lua
```lua
-- mysql-async (fetchAll + rows[1])
-- oxmysql
local row = MySQL.single.await(
'SELECT * FROM users WHERE identifier = ?',
{ identifier }
)
SELECT scalar (z. B. count, id)
```lua
```lua
-- mysql-async
-- oxmysql
local count = MySQL.scalar.await(
'SELECT COUNT(*) FROM owned_vehicles WHERE owner = ?',
{ owner }
)
INSERT (insertId erhalten)
```lua
```lua
-- mysql-async (execute)
-- oxmysql
local insertId = MySQL.insert.await(
'INSERT INTO notes (owner, text) VALUES (?, ?)',
{ cid, text }
)
UPDATE/DELETE (affectedRows)
```lua
```lua
-- mysql-async (execute)
-- oxmysql
local changed = MySQL.update.await(
'UPDATE users SET job = ?, job_grade = ? WHERE identifier = ?',
{ job, grade, identifier }
)
Transaktionen (manuell)
```lua
```lua
-- oxmysql manuelle Transaktion
MySQL.query.await('START TRANSACTION')
local ok = true
local r1 = MySQL.update.await('UPDATE users SET bank = bank - ? WHERE identifier = ? AND bank >= ?', { amount, fromId, amount })
local r2 = MySQL.update.await('UPDATE users SET bank = bank + ? WHERE identifier = ?', { amount, toId })
if r1 == 1 and r2 == 1 then
MySQL.query.await('COMMIT')
else
MySQL.query.await('ROLLBACK')
end
Einige Frameworks stellen Wrapper bereit (z. B. ox_lib)
Einige Frameworks stellen Wrapper bereit (z. B. ox_lib), die MySQL.ready, .transaction usw. hinzufügen. Die obigen Aufrufe sind ohne zusätzliche Wrapper sicher.
4) Prepared-Statements-Spickzettel
Param-Stile
mysql‑async (Legacy):@name-Platzhalter mit einer Tabelle: { ['@name']=value }
oxmysql (positional):?-Platzhalter mit einem Array: { value1, value2 }
oxmysql (benannt)::name-Platzhalter mit einem Objekt: { name = value }
Beispiele
```lua
```lua
-- Benannte Parameter (empfohlen für Lesbarkeit)
local row = MySQL.single.await(
'SELECT * FROM users WHERE identifier = :id',
{ id = identifier }
)
-- IN (...) Liste
-- Platzhalter dynamisch aufbauen und ein flaches Array übergeben
local ids = { 'cid1','cid2','cid3' }
local qs = ('?,' ):rep(#ids):sub(1,-2) -- "?, ?, ?"
local rows = MySQL.query.await('SELECT * FROM players WHERE citizenid IN ('..qs..')', ids)
-- JSON-Felder (MySQL 5.7+/MariaDB 10.2+)
local name = MySQL.scalar.await('SELECT JSON_UNQUOTE(JSON_EXTRACT(data, "$.name")) FROM players WHERE citizenid = ?', { cid })
Tun
Prepared Statements überall verwenden (niemals Benutzereingaben per String-Verkettung einbauen).
Benannte Parameter für Klarheit bei komplexen Statements bevorzugen.
LIMIT 1 hinzufügen, wenn eine einzelne Entität gelesen wird.
Vermeiden
Wildcard SELECT * in Hot Paths (nur benötigte Spalten projizieren).
```sql
```sql
-- Datenbank-Standard auf utf8mb4 setzen (Emoji-sicher)
ALTER DATABASE \`yourdb\` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
-- Gängige Tabellen konvertieren (Liste bei Bedarf erweitern)
ALTER TABLE \`users\` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE \`owned_vehicles\` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE \`players\` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE \`player_vehicles\` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
(B) MariaDB 10.4+ — gleiche Anweisungen sind gültig.
(B) MariaDB 10.4+ — gleiche Anweisungen sind gültig.
Weitere Hot-Tabellen (Inventory, Billing, Phone, Society, Jobs) nach Vorhandensein auf dem Server hinzufügen.
5.2 ESX-Indizes (sichere Performance-Gewinne)
MySQL 8+
```sql
```sql
ALTER TABLE \`users\`
ADD INDEX IF NOT EXISTS `idx_users_identifier` (`identifier`),
ADD INDEX IF NOT EXISTS `idx_users_job` (`job`),
ADD INDEX IF NOT EXISTS `idx_users_name` (`name`);
```sql
```sql
ALTER TABLE \`owned_vehicles\`
ADD UNIQUE INDEX IF NOT EXISTS `ux_owned_vehicles_plate` (`plate`),
ADD INDEX IF NOT EXISTS `idx_owned_vehicles_owner` (`owner`);
MariaDB 10.4+
```lua
```lua
-- Zuerst löschen für Idempotenz, wo IF NOT EXISTS nicht verfügbar ist
DROP INDEX IF EXISTS \`idx_users_identifier\` ON \`users\`;
DROP INDEX IF EXISTS \`idx_users_job\` ON \`users\`;
DROP INDEX IF EXISTS \`idx_users_name\` ON \`users\`;
CREATE INDEX \`idx_users_identifier\` ON \`users\` (\`identifier\`);
CREATE INDEX \`idx_users_job\` ON \`users\` (\`job\`);
CREATE INDEX \`idx_users_name\` ON \`users\` (\`name\`);
DROP INDEX IF EXISTS \`ux_owned_vehicles_plate\` ON \`owned_vehicles\`;
DROP INDEX IF EXISTS \`idx_owned_vehicles_owner\` ON \`owned_vehicles\`;
CREATE UNIQUE INDEX \`ux_owned_vehicles_plate\` ON \`owned_vehicles\` (\`plate\`);
CREATE INDEX \`idx_owned_vehicles_owner\` ON \`owned_vehicles\` (\`owner\`);
5.3 QBCore/QBOX-Indizes
MySQL 8+
```sql
```sql
ALTER TABLE \`players\`
ADD UNIQUE INDEX IF NOT EXISTS `ux_players_citizenid` (`citizenid`),
ADD INDEX IF NOT EXISTS `idx_players_license` (`license`),
ADD INDEX IF NOT EXISTS `idx_players_steam` (`steam`),
ADD INDEX IF NOT EXISTS `idx_players_last_name` (`lastname`);
```sql
```sql
ALTER TABLE \`player_vehicles\`
ADD UNIQUE INDEX IF NOT EXISTS `ux_player_vehicles_plate` (`plate`),
ADD INDEX IF NOT EXISTS `idx_player_vehicles_citizenid` (`citizenid`);
MariaDB 10.4+
```lua
```lua
DROP INDEX IF EXISTS \`ux_players_citizenid\` ON \`players\`;
DROP INDEX IF EXISTS \`idx_players_license\` ON \`players\`;
DROP INDEX IF EXISTS \`idx_players_steam\` ON \`players\`;
DROP INDEX IF EXISTS \`idx_players_last_name\` ON \`players\`;
CREATE UNIQUE INDEX \`ux_players_citizenid\` ON \`players\` (\`citizenid\`);
CREATE INDEX \`idx_players_license\` ON \`players\` (\`license\`);
CREATE INDEX \`idx_players_steam\` ON \`players\` (\`steam\`);
CREATE INDEX \`idx_players_last_name\` ON \`players\` (\`lastname\`);
DROP INDEX IF EXISTS \`ux_player_vehicles_plate\` ON \`player_vehicles\`;
DROP INDEX IF EXISTS \`idx_player_vehicles_citizenid\` ON \`player_vehicles\`;
CREATE UNIQUE INDEX \`ux_player_vehicles_plate\` ON \`player_vehicles\` (\`plate\`);
CREATE INDEX \`idx_player_vehicles_citizenid\` ON \`player_vehicles\` (\`citizenid\`);
5.4 Optional: ox_inventory (falls installiert)
```sql
```sql
ALTER TABLE \`ox_inventory\`
ADD INDEX IF NOT EXISTS `idx_inv_owner` (`owner`),
ADD INDEX IF NOT EXISTS `idx_inv_type` (`type`);
```sql
```sql
ALTER TABLE \`ox_inventory_items\`
ADD INDEX IF NOT EXISTS `idx_items_inv_owner_name` (`inventory`, `owner`, `name`);
Tabellennamen anpassen, wenn das Schema abweicht
Tabellennamen anpassen, wenn das Schema abweicht (manche Setups verwenden inventories / items).
In server.cfg tauschen: # ensure oxmysql ensure mysql-async
FXServer oder betroffene Ressourcen in Abhängigkeitsreihenfolge neu starten.
6.2 SQL-Rollback
Wenn nur Indizes hinzugefügt wurden: diese löschen (siehe die MariaDB-Blöcke oben — DROP INDEX IF EXISTS verwenden).
Wenn Charset/Collation geändert wurde und rückgängig gemacht werden muss, DB und Tabellen zurücksetzen:
```sql
```sql
ALTER DATABASE \`yourdb\` CHARACTER SET = utf8 COLLATE = utf8_general_ci;
ALTER TABLE \`users\` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE \`owned_vehicles\` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE \`players\` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE \`player_vehicles\` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
Wenn möglich aus backup.sql wiederherstellen, anstatt massenhafte Charset-Rücksetzungen vorzunehmen.
7) End-to-End-Migrationsverfahren (Skriptierbar)
Deploys einfrieren, DB sichern.
Abschnitt 5 „UP"-SQL im Staging anwenden → verifizieren → Prod.
Eine kleine Ressource, die du einfügen kannst, um Hot-Path-Queries auf deiner Hardware und deinem Datensatz zu vergleichen.
fxmanifest.lua
fx_version 'cerulean'
```lua
```lua
game 'gta5'
server_script 'bench.lua'
bench.lua
```lua
```lua
local COUNT = 2000 -- für deinen Server anpassen
local function bench(name, fn)
local t0 = os.clock()
local ok, err = pcall(fn)
local dt = (os.clock() - t0) * 1000.0
print(('[bench] %s: %.2f ms %s'):format(name, dt, ok and '' or ('ERR: '..tostring(err))))
```lua
```lua
end
-- Hot Path 1: Eigentümer-Lookup
bench('SELECT single', function()
for i=1,COUNT do
local row = MySQL.single.await('SELECT owner FROM owned_vehicles WHERE plate = :p LIMIT 1', { p = ('TEST%04d'):format(i % 500) })
end
end)
-- Hot Path 2: Batch-Fetch
bench('SELECT batch IN', function()
local ids = {}
for i=1,100 do ids[#ids+1] = ('cid%04d'):format(i) end
local qs = ('?,' ):rep(#ids):sub(1,-2)
local rows = MySQL.query.await('SELECT citizenid, firstname, lastname FROM players WHERE citizenid IN ('..qs..')', ids)
end)
-- Hot Path 3: Update mit Guard
bench('UPDATE guarded', function()
for i=1,COUNT do
local changed = MySQL.update.await('UPDATE users SET bank = bank + :d WHERE identifier = :id AND bank >= 0', { d = 1, id = ('lic:%04d'):format(i % 500) })
end
end)
Ausführen
Ausführen
Die Ressource in einen Ordner legen (z. B. ox-bench/), ensure ox-bench zur server.cfg hinzufügen.
Server-Konsole beobachten; Ergebnisse werden als Zeilen ausgegeben wie: [bench] SELECT single: 134.21 ms.
Für einen Vorher/Nachher-Vergleich einmal mit mysql-async ausführen (Aufrufe ggf. anpassen), dann mit oxmysql.
Worauf zu achten ist
Niedrigere Gesamt-ms pro Abschnitt nach der Migration.
Niedrigere P95/P99-Latenz bei Gameplay-Aktionen, die an Queries gebunden sind.
Weniger Slow-Query-Warnungen über eine Stunde Live-Play.
9) Fehlerbehebung
F: Ich erhalte „no such export: query/single/…".
A: oxmysql startet nicht früh genug. Sicherstellen, dass ensure oxmysql über den Ressourcen steht, die es verwenden.
F: Parameter-Fehler oder leere Ergebnisse.
A: Wahrscheinlich wurden @param-Platzhalter beibehalten. Durch ? oder :name ersetzen und entsprechend ein Array/Objekt übergeben.
F: Deadlocks oder partielle Schreibvorgänge.
A: Mehrstufige Geldbeträge/Transfers in einer Transaktion einwickeln (siehe Abschnitt 3), Indizes aus Abschnitt 5 hinzufügen.
F: JSON-Pfad gibt NULL zurück.
A: Prüfen, ob die Engine JSON-Funktionen unterstützt (MySQL ≥5.7/MariaDB ≥10.2) und ob der Spaltentyp JSON ist (nicht LONGTEXT).
F: Nach der Migration langsamer.
A: Fehlende Indizes prüfen, Query mit EXPLAIN analysieren, nur benötigte Spalten projizieren und das Server-Optimierungs-Playbook prüfen.
10) Code-Review-Checkliste (Kopieren/Einfügen)
Kein per String-Verkettung erstelltes SQL; alle Queries parametrisiert.
.single/.scalar mit LIMIT 1 verwenden, wenn nur eine Zeile/ein Wert benötigt wird.
IN (...)-Reads für Kollektionen batchen.
Transaktionen um mehrstufige Geld-/Inventory-Ops herum.
Index vorhanden für jede heiße WHERE/JOIN-Spalte.
SELECT * in Hot Paths vermeiden.
Slow Queries protokollieren; wöchentlich die Top-Übeltäter verfolgen.
Credits
Gepflegt von fivemx.com. Beiträge willkommen (Diffs zusätzlicher sicherer Indizes oder Wrapper-Helfer einsenden).
Was sind die SQL "UP"-Skripte und warum sollte ich sie ausführen, wenn ich zu `oxmysql` migriere?
Die SQL "UP"-Skripte enthalten Änderungen an deiner Datenbankstruktur, die empfohlen werden, um `oxmysql` optimal zu nutzen. Dazu gehören typischerweise Anpassungen des Zeichensatzes (Charset) und die Optimierung von Indizes. Diese Änderungen können die Performance verbessern und potenzielle Kompatibilitätsprobleme mit `oxmysql` verhindern. Es ist wichtig, diese Skripte vor der eigentlichen Migration auszuführen, da sie möglicherweise nicht abwärtskompatibel sind und Änderungen an der Datenbankstruktur vornehmen, die das alte `mysql-async` nicht unterstützt.
Wie funktioniert Von mysql-async zu oxmysql: Sichere Migration & Query-Muster?
Zielgruppe: FiveM-Server-Besitzer, Skripter, Maintainer Ziel: mysql-async sicher durch oxmysql ersetzen, Queries beschleunigen und SQL-Nutzung modernisieren.
Wie funktioniert Von mysql-async zu oxmysql: Sichere Migration & Query-Muster?
Zielgruppe: FiveM-Server-Besitzer, Skripter, Maintainer Ziel: mysql-async sicher durch oxmysql ersetzen, Queries beschleunigen und SQL-Nutzung modernisieren.
Was kostet Von mysql-async zu oxmysql: Sichere Migration & Query-Muster?
Zielgruppe: FiveM-Server-Besitzer, Skripter, Maintainer Ziel: mysql-async sicher durch oxmysql ersetzen, Queries beschleunigen und SQL-Nutzung modernisieren.