Ahorra hoy mismo en 20%. Usa el código WELCOME al finalizar la compra. WELCOME

De mysql-async a oxmysql: Migración segura y procesamiento de consultas…

Audiencia: Propietarios, programadores y mantenedores de servidores FiveM
Meta: Reemplazar MySQL asíncrono con oxmysql de forma segura, acelere las consultas y modernice su uso de SQL.

Lea también:


Resumen

  • Usar oxmysql:declaraciones preparadas, API de promesa/espera, mejores diagnósticos, sólido desempeño.
  • Cambios mínimos en el código: intercambio @param? (posicional) o :nombre parámetros (con nombre); reemplazar MySQL.Async.* llamadas con MySQL.*/exportaciones.oxmysql:*.
  • Ejecute los scripts SQL “UP” A continuación (correcciones de conjunto de caracteres/índice) y mantener el revertir práctico.
  • Verificar con el arnés de micro-benchmark al final para confirmar las victorias en tu hardware.

1) Lista de verificación de seguridad previa al vuelo

  1. Copia de seguridad completa: mysqldump --single-transaction yourdb > backup.sql.
  2. Entorno de ensayo esquema de producción reflejado + subconjunto de datos.
  3. Artefacto y dependencias:Compilación actual de FXServer, última oxmysql.
  4. Ventana de tiempo de inactividad para cambio de producto (generalmente < 5 minutos).
  5. Sondas de salud listo: /jugadores, flujo de inicio de sesión, operaciones económicas, operaciones de garaje, operaciones de inventario, controles de prohibición.

2) Instalar y cablear oxmysql

2.1 servidor.cfg

# Dejar de usar mysql-async default_prio 500 # asegurar mysql-async # ← comentar o eliminar # Iniciar oxmysql default_prio 50 asegurar oxmysql # Cadena de conexión consumida por oxmysql set mysql_connection_string "mysql://user:pass@127.0.0.1:3306/yourdb?charset=utf8mb4" # Diagnósticos opcionales set mysql_slow_query_warning 200 # registrar consultas más lentas que 200 ms set mysql_debug false # verdadero para registro detallado durante el ensayo

Mantener MySQL asíncrono deshabilitado pero disponible en su carpeta de recursos durante la fase de preparación (para una rápida reversión).

3) Mapeo de API: mysql‑async → oxmysql

MySQL asíncrono (legado):

  • Asíncrono: MySQL.Async.fetchAll, MySQL.Async.fetchScalar, MySQL.Async.execute
  • Sincronizar: MySQL.Sync.fetchAll, MySQL.Sync.fetchScalar, MySQL.Sync.execute
  • Parámetros: @param mesas de estilo como { ['@identificador']=identificador }

oxmysql (moderno):

  • Estilo de devolución de llamada a través de exportar: exportaciones.oxmysql:consulta|escalar|única|insertar|actualizar(sql, parámetros, cb)
  • Promesa/espera vía global: MySQL.query|scalar|single|insert|update.await(sql, parámetros) y devoluciones de llamadas sin espera sin .esperar
  • Parámetros: posicional ? a través de una matriz, o nombrado :nombre vía objeto

3.1 Reemplazos comunes

SELECCIONE muchos

-- mysql-async MySQL.Async.fetchAll( 'SELECT * FROM users WHERE identifier = @id', { ['@id'] = identifier }, function(rows) ... end ) -- oxmysql (devolución de llamada mediante exportación) 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 } )

SELECCIONAR una sola fila

-- mysql-async (fetchAll + rows[1]) -- oxmysql fila local = MySQL.single.await( 'SELECT * FROM users WHERE identificador = ?', { identificador } )

SELECCIONAR escalar (por ejemplo, contar, identificar)

-- mysql-async -- oxmysql recuento local = MySQL.scalar.await( 'SELECCIONAR CUENTA(*) DE vehículos_propios DONDE propietario = ?', { propietario } )

INSERTAR (obtener insertId)

-- mysql-async (ejecutar) -- oxmysql local insertId = MySQL.insert.await( 'INSERTAR EN notas (propietario, texto) VALORES (?, ?)', { cid, texto } )

ACTUALIZAR/ELIMINAR (filas afectadas)

-- mysql-async (ejecutar) -- oxmysql local changed = MySQL.update.await( 'ACTUALIZAR usuarios SET trabajo = ?, grado_trabajo = ? DONDE identificador = ?', { trabajo, grado, identificador } )

Actas (manual)

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

Algunos marcos exponen envoltorios (por ejemplo, biblioteca de buey) que añaden MySQL.listo, .transacción, etc. Las llamadas anteriores son seguras sin envoltorios adicionales.

4) Hoja de referencia para declaraciones preparadas

Estilos de parámetros

  • mysql‑async (heredado): @nombre marcadores de posición con una tabla: { ['@nombre']=valor }
  • oxmysql (posicional): ? marcadores de posición con un formación: { valor1, valor2 }
  • oxmysql (nombrado): :nombre marcadores de posición con un objeto: { nombre = valor }

Ejemplos

-- Parámetros nombrados (recomendados para legibilidad) fila local = MySQL.single.await( 'SELECT * FROM users WHERE identifier = :id', { id = identifier } ) -- EN (...) lista -- Construya marcadores de posición dinámicamente y pase una matriz plana ids locales = { 'cid1','cid2','cid3' } qs local = ('?,' ):rep(#ids):sub(1,-2) -- "?, ?, ?" filas locales = MySQL.query.await('SELECT * FROM jugadores WHERE citizenid IN ('..qs..')', ids) -- Campos JSON (MySQL 5.7+/MariaDB 10.2+) nombre local = MySQL.scalar.await('SELECT JSON_UNQUOTE(JSON_EXTRACT(data, "$.name")) FROM jugadores WHERE citizenid = ?', { cid })

Hacer

  • Usar declaraciones preparadas en todas partes (nunca concatene cadenas de entrada del usuario).
  • Preferir parámetros nombrados para mayor claridad en afirmaciones complejas.
  • Agregar LÍMITE 1 al leer una sola entidad.

Evitar

  • Comodín SELECCIONAR * en caminos calientes (el proyecto necesitaba columnas).
  • Consultas N+1 por fila; lote con EN (...).

5) Scripts de migración de base de datos “UP” (listos para ejecutar)

Seleccione los bloques compatibles con su framework (ESX/QBCore) y servidor (MySQL 8+ o MariaDB 10.4+). Ejecútelos primero en la configuración.

5.1 Normalizar el conjunto de caracteres y la intercalación (UTF‑8 en todas partes)

(A) MySQL 8+ - reemplazar tu base de datos una vez

-- Forzar la base de datos predeterminada a utf8mb4 (segura para emojis) ALTER DATABASE `yourdb` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; -- Convertir tablas comunes (ampliar la lista según sea necesario) 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+ —Las mismas afirmaciones son válidas.

Agregue otras tablas activas (inventario, facturación, teléfono, sociedad, trabajos) tal como están presentes en su servidor.

5.2 Índices ESX (el rendimiento seguro es lo mejor)

MySQL 8+

ALTERAR TABLA `usuarios` AGREGAR ÍNDICE SI NO EXISTE `idx_users_identifier` (`identificador`), AGREGAR ÍNDICE SI NO EXISTE `idx_users_job` (`trabajo`), AGREGAR ÍNDICE SI NO EXISTE `idx_users_name` (`nombre`); ALTERAR TABLA `vehicles_propiedad` AGREGAR ÍNDICE ÚNICO SI NO EXISTE `ux_vehicles_propiedad_placa` (`placa`), AGREGAR ÍNDICE SI NO EXISTE `idx_vehicles_propiedad_propietario` (`propietario`);

MariaDB 10.4+

-- Eliminar primero para que sea idempotente donde SI NO EXISTE no está disponible ELIMINAR ÍNDICE SI EXISTE `idx_users_identifier` EN `usuarios`; ELIMINAR ÍNDICE SI EXISTE `idx_users_job` EN `usuarios`; ELIMINAR ÍNDICE SI EXISTE `idx_users_name` EN `usuarios`; CREAR ÍNDICE `idx_users_identifier` EN `usuarios` (`identificador`); CREAR ÍNDICE `idx_users_job` EN `usuarios` (`trabajo`); CREAR ÍNDICE `idx_users_name` EN `usuarios` (`nombre`); ELIMINAR ÍNDICE SI EXISTE `ux_owned_vehicles_plate` EN `owned_vehicles`; ELIMINAR ÍNDICE SI EXISTE `idx_owned_vehicles_owner` EN `owned_vehicles`; CREAR ÍNDICE ÚNICO `ux_owned_vehicles_plate` EN `owned_vehicles` (`plate`); CREAR ÍNDICE `idx_owned_vehicles_owner` EN `owned_vehicles` (`owner`);

5.3 Índices QBCore/QBOX

MySQL 8+

ALTERAR TABLA `players` AGREGAR ÍNDICE ÚNICO SI NO EXISTE `ux_players_citizenid` (`citizenid`), AGREGAR ÍNDICE SI NO EXISTE `idx_players_license` (`license`), AGREGAR ÍNDICE SI NO EXISTE `idx_players_steam` (`steam`), AGREGAR ÍNDICE SI NO EXISTE `idx_players_last_name` (`lastname`); ALTERAR TABLA `player_vehicles` AGREGAR ÍNDICE ÚNICO SI NO EXISTE `ux_player_vehicles_plate` (`plate`), AGREGAR ÍNDICE SI NO EXISTE `idx_player_vehicles_citizenid` (`citizenid`);

MariaDB 10.4+

SOLTAR ÍNDICE SI EXISTE `ux_players_citizenid` EN `players`; SOLTAR ÍNDICE SI EXISTE `idx_players_license` EN `players`; SOLTAR ÍNDICE SI EXISTE `idx_players_steam` EN `players`; SOLTAR ÍNDICE SI EXISTE `idx_players_last_name` EN `players`; CREAR ÍNDICE ÚNICO `ux_players_citizenid` EN `players` (`citizenid`); CREAR ÍNDICE `idx_players_license` EN `players` (`license`); CREAR ÍNDICE `idx_players_steam` EN `players` (`steam`); CREAR ÍNDICE `idx_players_last_name` EN `players` (`lastname`); SOLTAR ÍNDICE SI EXISTE `ux_player_vehicles_plate` EN `player_vehicles`; SOLTAR ÍNDICE SI EXISTE `idx_player_vehicles_citizenid` EN `player_vehicles`; CREAR ÍNDICE ÚNICO `ux_player_vehicles_plate` EN `player_vehicles` (`plate`); CREAR ÍNDICE `idx_player_vehicles_citizenid` EN `player_vehicles` (`citizenid`);

5.4 Opcional: ox_inventory (si está instalado)

ALTERAR TABLA `ox_inventory` AGREGAR ÍNDICE SI NO EXISTE `idx_inv_owner` (`propietario`), AGREGAR ÍNDICE SI NO EXISTE `idx_inv_type` (`tipo`); ALTERAR TABLA `ox_inventory_items` AGREGAR ÍNDICE SI NO EXISTE `idx_items_inv_owner_name` (`inventario`, `propietario`, `nombre`);

Ajuste los nombres de las tablas si su esquema es diferente (algunas configuraciones utilizan inventarios / elementos).

6) Plan de reversión (sin pánico)

6.1 Reversión de código

  1. Revierte los cambios en tus recursos (mantén un legado-mysql-async rama).
  2. En servidor.cfg intercambio: # garantizar oxmysql garantizar mysql-async
  3. Reinicie FXServer o los recursos afectados en orden de dependencia.

6.2 Reversión de SQL

  • Si solo tu índices añadidos:déjalos caer (ver el MariaDB bloques arriba — uso Eliminar índice si existe).
  • Si usted cambio de conjunto de caracteres/intercalación y debe deshacer, revertir la base de datos y las tablas:
ALTERAR BASE DE DATOS `yourdb` CONJUNTO DE CARACTERES = utf8 INTERCOLARD = utf8_general_ci; ALTERAR TABLA `usuarios` CONVERTIR A CONJUNTO DE CARACTERES utf8 INTERCOLARD utf8_general_ci; ALTERAR TABLA `vehículos_propiedad` CONVERTIR A CONJUNTO DE CARACTERES utf8 INTERCOLARD utf8_general_ci; ALTERAR TABLA `jugadores` CONVERTIR A CONJUNTO DE CARACTERES utf8 INTERCOLARD utf8_general_ci; ALTERAR TABLA `vehículos_jugadores` CONVERTIR A CONJUNTO DE CARACTERES utf8 INTERCOLARD utf8_general_ci;

Prefiero restaurar desde copia de seguridad.sql en lugar de inversiones masivas de caracteres cuando sea posible.

7) Procedimiento de migración de extremo a extremo (programable)

  1. Congelación de despliegues, realizar una copia de seguridad de la base de datos.
  2. Aplicar Sección 5 SQL “UP” en puesta en escena → verificar → prod.
  3. Refactorización del código de confirmación: reemplazar llamadas (Sección 3) + estilos de parámetros (Sección 4).
  4. Desplegar, garantizar oxmysql, reiniciar el servidor.
  5. Correr pruebas de humo (inicio de sesión, cheques de pago, inventario, aparición/desaparición de vehículos, prohibiciones, dinero de la sociedad, alternancia de deberes laborales).
  6. Observe los registros durante 15 a 30 minutos (advertencia de consulta lenta de mysql ayuda); solucione cualquier parámetro faltante o desajuste del esquema.

8) Micro-Benchmarks (Traiga sus propios números)

Un pequeño recurso que puedes usar para comparar consultas de rutas activas en su hardware y conjunto de datos.

fxmanifest.lua

fx_version 'cerulean' juego 'gta5' script_servidor 'bench.lua'

banco.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)

Cómo correr

  1. Coloque el recurso en una carpeta (por ejemplo, banco de bueyes/), agregar asegurar el banco de bueyes a servidor.cfg.
  2. Consola del servidor Tail; los resultados se imprimen como líneas como: [banco] SELECCIONAR único: 134,21 ms.
  3. Para una antes/después comparación, ejecutar una vez con MySQL asíncrono (ajustar las llamadas si es necesario), luego con oxmysql.

Qué buscar

  • Menor cantidad total de ms por sección después de la migración.
  • Menor latencia P95/P99 en acciones de juego vinculadas a consultas.
  • Menos advertencias de consultas lentas durante una hora de juego en vivo.

9) Solución de problemas

P: Recibo el mensaje “no existe dicha exportación: consulta/única/…”.
A: oxmysql no se inicia con la suficiente antelación. Asegúrese garantizar oxmysql está por encima de los recursos que lo utilizan.

P: Errores de parámetros o resultados vacíos.
A: Probablemente lo mantuviste @param marcadores de posición. Reemplazar con ? o :nombre y pasar una matriz/objeto en consecuencia.

P: Bloqueos o escrituras parciales.
A: Envuelva saldos/transferencias de varios pasos en una transacción (consulte la Sección 3) y agregue índices de la Sección 5.

P: La ruta JSON devuelve NULL.
A: Confirme que su motor admita funciones JSON (MySQL ≥5.7/MariaDB ≥10.2) y que el tipo de columna sea JSON (no TEXTO LARGO).

P: Lento después de la migración.
A: Verifique los índices faltantes, EXPLICAR su consulta, el proyecto solo necesita columnas y revise el Manual de optimización del servidor.

10) Lista de verificación de revisión de código (copiar y pegar)

  • No hay SQL concatenado de cadenas; todas las consultas están parametrizadas.
  • Usar .soltero/.escalar con LÍMITE 1 cuando solo se requiere una fila/valor.
  • Lote EN (...) lee para colecciones.
  • Transacciones en torno a operaciones de dinero/inventario de múltiples pasos.
  • Índice presente para cada hot DÓNDE/UNIRSE columna.
  • Evitar SELECCIONAR * en caminos calientes.
  • Registrar consultas lentas; realizar un seguimiento semanal de los principales infractores.

Enlaces internos


Créditos
Mantenido por fivemx.com. Se agradecen las contribuciones (envíe comparaciones de índices seguros adicionales o ayudantes de wrapper).

Lucas
Lucas

Soy Luke, gamer y me encanta escribir sobre FiveM, GTA y juegos de rol. Dirijo una comunidad de juegos de rol y tengo unos 10 años de experiencia administrando servidores.

Artículos: 436