$ USD
  • $ USD
  • € EUR
  • Britse pond (£ GBP)
  • $ AUD
  • R$ BRL
  • CHF CHF
  • ¥ JPY
mysql async oxmysql

Van mysql-async naar oxmysql: Veilige migratie & query P…

Publiek: FiveM server-eigenaren, scripters, beheerders
Doel: Vervangen mysql-async met oxmysql veilig, versnel query's en moderniseer je SQL-gebruik.

Lees ook:


TL;DR

  • Gebruik oxmysql: voorbereide statements, promise/await API, betere diagnostiek, sterke prestaties.
  • Minimale codeaanpassingen: vervang @param? (positioneel) of :name (benoemde) parameters; vervang MySQL.Async.* aanroepen door MySQL.*/exports.oxmysql:*.
  • Voer de SQL “UP”-scripts uit hieronder (charset/index-fixes) en houd de rollback bij de hand.
  • Verifieer met de micro-benchmark-opstelling aan het einde om winst op jouw hardware te bevestigen.

1) Veiligheidscheck voor vertrek

  1. Volledige back-up: mysqldump --single-transaction yourdb > backup.sql.
  2. Staging-omgeving die de productieschema + gegevenssubset weerspiegelt.
  3. Artifact & afhankelijkheden: Huidige FXServer build, nieuwste oxmysql.
  4. Downtime-venster voor productieomschakeling (meestal < 5 minuten).
  5. Gezondheidscontroles gereed: /players, inlogstroom, economie-operaties, garage-operaties, inventaris-operaties, bancontroles.

2) Installeren & Aansluiten oxmysql

2.1 server.cfg

# Stop using mysql-async
default_prio 500
# ensure mysql-async # ← comment out or remove

# Start oxmysql
default_prio 50
ensure oxmysql

# Connection string consumed by oxmysql
set mysql_connection_string "mysql://user:pass@127.0.0.1:3306/yourdb?charset=utf8mb4"

# Optional diagnostics
set mysql_slow_query_warning 200 # log queries slower than 200ms
set mysql_debug false # true for verbose logging during staging

Houden mysql-async uitgeschakeld maar beschikbaar in je resources-map tijdens de stagingfase (voor snelle terugdraaiing).

3) API Mapping: mysql‑async → oxmysql

mysql-async (verouderd):

  • Async: MySQL.Async.fetchAll, MySQL.Async.fetchScalar, MySQL.Async.execute
  • Sync: MySQL.Sync.fetchAll, MySQL.Sync.fetchScalar, MySQL.Sync.execute
  • Parameters: @param tabellen opmaken zoals { ['@identifier']=identifier }

oxmysql (modern):

  • Callback-stijl 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) en non-await callbacks zonder .await
  • Parameters: positioneel ? via array, of genaamd :name via object

3.1 Veelvoorkomende vervangingen

SELECT meerdere

-- 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 enkele rij

-- mysql-async (fetchAll + rows[1])

-- oxmysql
local row = MySQL.single.await(
 'SELECT * FROM users WHERE identifier = ?',
 { identifier }
)

SELECT scalar (bijv., count, id)

-- mysql-async

-- oxmysql
local count = MySQL.scalar.await(
 'SELECT COUNT(*) FROM owned_vehicles WHERE owner = ?',
 { owner }
)

INVOEGEN (insertId ophalen)

-- mysql-async (execute)

-- oxmysql
local insertId = MySQL.insert.await(
 'INSERT INTO notes (owner, text) VALUES (?, ?)',
 { cid, text }
)

UPDATE/DELETE (affectedRows)

-- mysql-async (execute)

-- oxmysql
local changed = MySQL.update.await(
 'UPDATE users SET job = ?, job_grade = ? WHERE identifier = ?',
 { job, grade, identifier }
)

Transacties (handmatig)

-- oxmysql manual transaction
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

Sommige frameworks bieden wrappers aan (bijv., ox_lib) die toevoegen MySQL.ready, .transaction, etc. De bovenstaande aanroepen zijn veilig zonder extra wrappers.

4) Cheat‑Sheet voor voorbereide statements

Param-stijlen

  • mysql-async (legacy): @name placeholders met een tabel: { ['@name']=value }
  • oxmysql (positioneel): ? placeholders met een array: { value1, value2 }
  • oxmysql (genoemd): :name placeholders met een object: { name = value }

Voorbeelden

-- Named params (recommended for readability)
local row = MySQL.single.await(
 'SELECT * FROM users WHERE identifier = :id',
 { id = identifier }
)

-- IN (...) list
-- Build placeholders dynamically and pass a flat array
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 fields (MySQL 5.7+/MariaDB 10.2+)
local name = MySQL.scalar.await('SELECT JSON_UNQUOTE(JSON_EXTRACT(data, "$.name")) FROM players WHERE citizenid = ?', { cid })

Doe

  • Gebruik prepared statements overal (gebruik nooit string-concatenatie van gebruikersinvoer).
  • Geef de voorkeur aan genormaliseerde parameters voor duidelijkheid in complexe statements.
  • Toevoegen LIMIT 1 bij het lezen van een enkele entiteit.

Vermijd

  • Wildcard SELECT * in hot paths (projecteer benodigde kolommen).
  • N+1 queries per rij; batch met IN (...).

5) Database “UP” Migratiescripts (Klaar om te draaien)

Kies de blokken die bij jouw framework (ESX/QBCore) en server (MySQL 8+ of MariaDB 10.4+) passen. Test eerst op staging.

5.1 Normaliseer Tekenset & Collator (UTF‑8 overal)

(A) MySQL 8+ — vervangen yourdb eenmalig

-- Force database default to utf8mb4 (emoji‑safe)
ALTER DATABASE `yourdb` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;

-- Convert common tables (extend list as needed)
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+ — dezelfde uitspraken zijn geldig.

Voeg andere veelgebruikte tabellen toe (inventory, billing, phone, society, jobs) zoals aanwezig op jouw server.

5.2 ESX Indexen (veilige prestatiewinst)

MySQL 8+

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`);

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+

-- Drop first to be idempotent where IF NOT EXISTS is unavailable
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 Indexen

MySQL 8+

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`);

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+

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 Optioneel: ox_inventory (indien geïnstalleerd)

ALTER TABLE `ox_inventory`
 ADD INDEX IF NOT EXISTS `idx_inv_owner` (`owner`),
 ADD INDEX IF NOT EXISTS `idx_inv_type` (`type`);

ALTER TABLE `ox_inventory_items`
 ADD INDEX IF NOT EXISTS `idx_items_inv_owner_name` (`inventory`, `owner`, `name`);

Pas tabelnamen aan als uw schema verschilt (sommige installaties gebruiken inventories / items).

6) Rollbackplan (Zero‑Panic)

6.1 Code rollback

  1. Keer je resource-wijzigingen terug (bewaar een legacy-mysql-async branch).
  2. In server.cfg wissel: # ensure oxmysql ensure mysql-async
  3. Herstart FXServer of de getroffen resources in afhankelijkheidsvolgorde.

6.2 SQL rollback

  • Als je alleen toegevoegde indexen: verwijder ze (zie de MariaDB blocks hierboven — gebruik DROP INDEX IF EXISTS).
  • Als je veranderde charset/collation en moet ongedaan maken, de DB en tabellen terugdraaien:
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;

Herstel bij voorkeur vanuit backup.sql in plaats van massale charset-omkeringen waar mogelijk.

7) End-to-End Migratieprocedure (Scriptbaar)

  1. Bevries deploys, maak een back-up van de DB.
  2. Toepassen Sectie 5 “UP” SQL op staging → verifiëren → prod.
  3. Commit code refactor: vervang aanroepen (Sectie 3) + parameterstijlen (Sectie 4).
  4. Implementeren, ensure oxmysql, start de server opnieuw op.
  5. Voer smoke tests (login, salarisstrookjes, inventaris, voertuig spawnen/despawnen, verbanningen, maatschappijgeld, job duty schakelaars).
  6. Bekijk logs gedurende 15-30 minuten (mysql_slow_query_warning helpt); los eventuele gemiste parameters of schema-mismatches op.

8) Microbenchmarks (Breng je eigen cijfers mee)

Een kleine resource die je kunt toevoegen om hot-path queries te vergelijken jouw hardware en dataset.

fxmanifest.lua

fx_version 'cerulean'
game 'gta5'
server_script 'bench.lua'

bench.lua

local COUNT = 2000 -- adjust for your server

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))))
end

-- Hot path 1: ownership 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: batched 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 with 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)

Hoe te draaien

  1. Plaats de resource in een map (bijv. ox-bench/), toevoegen ensure ox-bench naar server.cfg.
  2. Bekijk de server console; resultaten worden als regels weergegeven zoals: [bench] SELECT single: 134.21 ms.
  3. Voor een voor/na ter vergelijking, voer een keer uit met mysql-async (pas de aanroepen indien nodig aan), dan met oxmysql.

Waar je op moet letten

  • Lagere totale ms per sectie na migratie.
  • Lagere P95/P99-latentie bij gameplay-acties die aan queries zijn gekoppeld.
  • Minder slow‑query waarschuwingen gedurende een uur live spelen.

9) Problemen oplossen

V: Ik krijg “no such export: query/single/…”.
A: oxmysql niet vroeg genoeg wordt gestart. Zorg ervoor dat ensure oxmysql staat boven bronnen die het gebruiken.

V: Parameterfouten of lege resultaten.
A: Je hebt waarschijnlijk @param placeholders. Vervang door ? of :name en geef dienovereenkomstig een array/object door.

V: Deadlocks of gedeeltelijke schrijfbewerkingen.
A: Wikkel multi-step saldi/overdrachten in een transactie (zie Sectie 3), voeg indexen toe uit Sectie 5.

V: JSON-pad retourneert NULL.
A: Bevestig dat je engine JSON-functies ondersteunt (MySQL ≥5.7/MariaDB ≥10.2) en dat het kolomtype is JSON (niet LONGTEXT).

V: Traag na migratie.
A: Controleer ontbrekende indexes, EXPLAIN jouw query, projecteer alleen benodigde kolommen en raadpleeg het Server Optimalisatie Draaiboek.

10) Code Review Checklist (kopiëren/plakken)

  • Geen string‑concatenatie SQL; alle queries geparameteriseerd.
  • Gebruik .single/.scalar met LIMIT 1 wanneer slechts één rij/waarde vereist is.
  • Batch IN (...) leest voor collecties.
  • Transacties rond meerstaps geld/inventory-operaties.
  • Index aanwezig voor elke hot WHERE/JOIN kolom.
  • Vermijd SELECT * in hot paths.
  • Log langzame queries; volg wekelijks de grootste overtreders.

Interne Links


Credits
Onderhouden door fivemx.com. Bijdragen welkom (stuur diffs van extra veilige indexen of wrapper-helpers).