oxmysql คืออะไร ทำไม FiveM ต้องใช้

oxmysql คือ Database Resource สำหรับ FXServer/FiveM ที่ทำหน้าที่เป็นตัวกลางระหว่าง Server Scripts กับฐานข้อมูล MySQL/MariaDB

พูดง่าย ๆ คือ เมื่อ FiveM Server ต้องการบันทึกข้อมูลอย่าง

ตัวละคร
เงิน
อาชีพ
รถ
บ้าน
Inventory
โทรศัพท์
ธุรกิจ
Gang
บัญชีธนาคาร

Script ฝั่ง Server จะส่งคำสั่ง SQL ผ่าน oxmysql ไปยัง Database

โครงสร้างพื้นฐานคือ

FiveM Script
↓
oxmysql
↓
MariaDB / MySQL
↓
Database Tables

ดังนั้น oxmysql ไม่ใช่ฐานข้อมูล แต่เป็น ตัวเชื่อม FiveM กับฐานข้อมูล

① oxmysql คืออะไรแบบง่ายที่สุด

สมมติ Player มีเงินในธนาคาร

Bank = $50,000

เมื่อ Player ฝากเพิ่ม

$10,000

Server ต้องเปลี่ยนข้อมูลเป็น

Bank = $60,000

และต้องจำค่าไว้แม้ Player

ออกเกม
เข้าใหม่
Restart Server

Script จึงต้องบันทึกข้อมูลลง Database

เช่น

UPDATE users
SET bank = ?
WHERE identifier = ?

oxmysql ทำหน้าที่ส่ง Query นี้จาก FXServer ไปยัง Database

② oxmysql ไม่ใช่ Database

นี่เป็นจุดที่มือใหม่มักสับสน

oxmysql
≠ Database Server

และ

oxmysql
≠ MariaDB

โครงสร้างจริงคือ

FiveM
↓
oxmysql
↓
MariaDB

ดังนั้นแม้ติดตั้ง oxmysql แล้ว ถ้าไม่มี Database Server ระบบก็ยังเก็บข้อมูลไม่ได้

③ MariaDB คืออะไร

MariaDB คือ Database Server ที่เก็บข้อมูลจริง

ตัวอย่าง Tables

users
players
vehicles
owned_vehicles
ox_inventory
phone_messages
bank_accounts

ภายใน Tables จะมี Rows ของ Players และข้อมูลต่าง ๆ

oxmysql เป็นเพียง Layer ที่ใช้ส่งคำสั่งไปยัง MariaDB

④ MySQL ใช้ได้หรือไม่

ใช้ได้ แต่ Documentation ปัจจุบันของ oxmysql แนะนำ MariaDB

เหตุผลหนึ่งคือ FiveM Resources จำนวนมากถูกพัฒนามาจาก Ecosystem ที่เดิมใช้ MySQL 5.7 และอาจพบ Compatibility Differences เมื่อใช้ MySQL 8

เช่น

Reserved keywords เพิ่มขึ้น
JSON/LONGTEXT behavior บางส่วนต่างกัน

ดังนั้นสำหรับ FiveM Server ใหม่ การใช้ MariaDB ตาม Current Documentation เป็นตัวเลือกที่เหมาะกว่า

⑤ XAMPP ควรใช้ไหม

Current oxmysql Documentation ระบุชัดว่า ไม่แนะนำให้ติดตั้ง XAMPP เพียงเพื่อใช้ Database กับ FiveM

XAMPP เป็น Development Web Server Stack ที่รวมหลายระบบ เช่น

Apache
PHP
MariaDB/MySQL

แต่ FiveM Server ต้องการ Database Server เป็นหลัก

สำหรับ Production จึงควรติดตั้ง

MariaDB

โดยตรง

⑥ FiveM จำเป็นต้องใช้ oxmysql ทุก Server หรือไม่

ไม่จำเป็น

ถ้าเป็น Server ง่าย ๆ เช่น

Drift
Freeroam
Deathmatch
Temporary Game Mode

ที่ไม่ต้องเก็บข้อมูล Persistent อาจไม่ต้องมี Database เลย

แต่ RP Server ส่วนใหญ่มักต้องเก็บ

Characters
Money
Jobs
Vehicles
Inventory
Housing
Phone
Businesses

จึงแทบหลีกเลี่ยง Database ไม่ได้

และหาก Framework/Resources ใช้ oxmysql ก็ต้องติดตั้งมัน

⑦ ทำไม FiveM RP ต้องมี Database

ลองคิดว่าไม่มี Database

Player เข้า Server

สร้างตัวละคร
หาเงิน
ซื้อรถ
ซื้อบ้าน
เก็บ Item

จากนั้นออกเกม

ถ้าทุกอย่างอยู่ใน Memory อย่างเดียว เมื่อ Server Restart ข้อมูลอาจหายทั้งหมด

Database ทำให้ข้อมูลเป็น

Persistent Data

คือสามารถอยู่ต่อหลัง Session จบ

⑧ oxmysql ทำหน้าที่ตรงไหน

Architecture ของ RP Server อาจเป็น

Player
↓
FiveM Client
↓
Server Script
↓
Framework
↓
oxmysql
↓
MariaDB

ตัวอย่าง

Player ซื้อรถ
↓
Dealership Script
↓
ตรวจเงิน
↓
หักเงิน
↓
สร้าง Vehicle Record
↓
oxmysql
↓
Database

เมื่อ Player เข้าใหม่ รถจึงยังอยู่

⑨ Framework ไหนใช้ oxmysql

Framework ปัจจุบันหลายตัวใช้ oxmysql

เช่น

ESX Legacy
QBCore
Qbox
ox_core

รวมถึง Resources จำนวนมากใน OX ecosystem

ดังนั้น oxmysql ไม่ได้จำกัดอยู่กับ Framework ใด Framework หนึ่ง

⑩ ESX ใช้ oxmysql หรือไม่

Current ESX Legacy Core ใช้

@oxmysql/lib/MySQL.lua

ใน Server Scripts และประกาศ oxmysql เป็น Dependency

ดังนั้น Server ESX รุ่นปัจจุบันจำนวนมากใช้ Stack

MariaDB
↓
oxmysql
↓
es_extended

ไม่ควรยึด Tutorial ESX เก่าที่ใช้ mysql-async เป็นมาตรฐานปัจจุบันโดยไม่ตรวจ Version

⑪ QBCore ใช้ oxmysql หรือไม่

Current qb-core ก็ใช้

@oxmysql/lib/MySQL.lua

และมี oxmysql เป็น Dependency

Architecture โดยทั่วไปคือ

MariaDB
↓
oxmysql
↓
qb-core

จากนั้น Resources ใน QBCore Ecosystem จึงเข้าถึงข้อมูลผ่าน Framework หรือ Database APIs ตาม Design

⑫ Qbox ใช้ oxmysql หรือไม่

ใช้

Qbox/OX-first Stack โดยทั่วไปจะเห็น

MariaDB
↓
oxmysql
↓
ox_lib
↓
qbx_core
↓
ox_inventory

ดังนั้น Database Layer เป็นส่วนพื้นฐานของ Qbox Server

⑬ ox_core ใช้ oxmysql หรือไม่

ใช้เช่นกัน

ox_core ต้องเก็บข้อมูลอย่าง

Users
Characters
Groups
Accounts
Vehicles

จึงต้องมี Persistent Database Layer

โดยมี oxmysql เป็นตัวกลางระหว่าง Framework กับ MariaDB

⑭ oxmysql ใช้เทคโนโลยีอะไร

Current Project อธิบายว่า oxmysql เป็น

FiveM resource to communicate with a MySQL database
using node-mysql2

กล่าวคือ Core Database Communication ใช้ mysql2 ใน Node.js ecosystem

Current Package Version ที่ตรวจสอบในปี 2026 คือ

oxmysql 2.14.1

โดย Release v2.14.1 ออกวันที่ 4 พฤษภาคม 2026

Version สามารถเปลี่ยนในอนาคต จึงควรตรวจ Current Release ตอนติดตั้งจริง

⑮ Script Lua เรียก oxmysql อย่างไร

วิธีที่ Documentation แนะนำคือเพิ่มใน fxmanifest.lua

server_script '@oxmysql/lib/MySQL.lua'

และต้องวางบรรทัดนี้ก่อน Server Scripts ที่เรียก MySQL

ตัวอย่าง

fx_version 'cerulean'
game 'gta5'

server_script '@oxmysql/lib/MySQL.lua'

server_script 'server.lua'

dependency 'oxmysql'

จากนั้นใน server.lua สามารถเรียก

MySQL.query.await(...)

ได้

⑯ ทำไมต้อง Import MySQL.lua

ไฟล์

@oxmysql/lib/MySQL.lua

สร้าง Interface

MySQL

ให้ Lua Resource

จากนั้นสามารถใช้ Methods เช่น

MySQL.query
MySQL.single
MySQL.scalar
MySQL.insert
MySQL.update
MySQL.prepare
MySQL.transaction

แทนการเรียก Raw Exports ทุกครั้ง

⑰ MySQL.query คืออะไร

ใช้ Execute Query และคืนข้อมูลที่ตรงกับ Query

ตัวอย่าง

local players = MySQL.query.await(
    'SELECT `identifier`, `name` FROM `users`'
)

ถ้ามีหลาย Rows จะได้ข้อมูลกลับมาเป็น Table

เหมาะกับ

SELECT หลาย Records
ค้นรายการรถ
โหลดรายการธุรกิจ
โหลด Characters

⑱ ตัวอย่าง Query แบบมี WHERE

local result = MySQL.query.await(
    'SELECT * FROM `users` WHERE `identifier` = ?',
    { identifier }
)

ค่า

?

คือ Placeholder

ค่าจริงส่งแยกใน Parameters

{ identifier }

วิธีนี้ดีกว่าการต่อ String จาก Input โดยตรง

⑲ ทำไมควรใช้ Parameters

ไม่ควรเขียนแบบนี้

local query = "SELECT * FROM users WHERE identifier = '" .. identifier .. "'"

โดยเฉพาะถ้าข้อมูลส่วนใดมาจาก User Input

ควรใช้

MySQL.query.await(
    'SELECT * FROM users WHERE identifier = ?',
    { identifier }
)

Parameterized Queries ช่วยจัดการ Parameters อย่างถูกต้องและลดความเสี่ยง SQL Injection เมื่อใช้งานอย่างเหมาะสม

⑳ SQL Injection คืออะไร

สมมติ Script นำข้อความจาก User ไปต่อ SQL ตรง ๆ

User Input
↓
SQL String
↓
Database

ผู้ไม่หวังดีอาจพยายามใส่ SQL Syntax เพิ่มเข้าไป

จึงควรใช้

Placeholders
+
Parameters

แทนการต่อ User Input เข้า Query โดยตรง

แต่ Security ยังต้องรวมถึง

Validation
Authorization
Permissions
Data Type Checking

ด้วย

㉑ MySQL.single คืออะไร

ใช้เมื่อต้องการ Row เดียว

ตัวอย่าง

local player = MySQL.single.await(
    'SELECT `firstname`, `lastname` FROM `users` WHERE `identifier` = ? LIMIT 1',
    { identifier }
)

if not player then
    return
end

print(player.firstname, player.lastname)

ต่างจาก query ที่คืนรายการ Rows

single เหมาะเมื่อเราคาดหวัง Record เดียว

㉒ query กับ single ต่างกันอย่างไร

query

อาจคืนหลาย Rows

Row 1
Row 2
Row 3

single

คืน

Row เดียว

ดังนั้นถ้าค้น Player จาก Unique Identifier

single

มักอ่าน Code ง่ายกว่า Query แล้วใช้ [1]

㉓ MySQL.scalar คืออะไร

scalar คืนค่าของ Column แรกจาก Row แรก

ตัวอย่างต้องการ Bank Balance อย่างเดียว

local balance = MySQL.scalar.await(
    'SELECT `bank` FROM `users` WHERE `identifier` = ?',
    { identifier }
)

ผลอาจเป็น

50000

แทน Table ทั้ง Row

㉔ scalar เหมาะกับอะไร

เหมาะกับ Query เช่น

COUNT(*)
SUM(...)
ชื่อเดียว
ยอดเงินเดียว
Status เดียว

ตัวอย่าง

local count = MySQL.scalar.await(
    'SELECT COUNT(*) FROM `vehicles` WHERE `owner` = ?',
    { identifier }
)

ไม่จำเป็นต้อง Query Row เต็มหากต้องการค่าเดียว

㉕ MySQL.insert คืออะไร

ใช้เพิ่ม Row แล้วคืน

Insert ID

ถ้ามี Auto Increment ID

ตัวอย่าง

local id = MySQL.insert.await(
    'INSERT INTO `characters` (`identifier`, `firstname`) VALUES (?, ?)',
    {
        identifier,
        firstName
    }
)

print(id)

เหมาะกับการสร้าง Record ใหม่

㉖ MySQL.update คืออะไร

ใช้สำหรับ Query ที่ต้องการรู้จำนวน Rows ที่ได้รับผลกระทบ

ตัวอย่าง

local affectedRows = MySQL.update.await(
    'UPDATE `users` SET `bank` = ? WHERE `identifier` = ?',
    {
        balance,
        identifier
    }
)

ผลลัพธ์อาจเป็น

0
1
2
...

ตามจำนวน Rows ที่ถูกแก้

㉗ update ใช้กับ DELETE ได้หรือไม่

แนวคิดของ Method นี้เหมาะกับ Statements ที่ต้องการ

affectedRows

เช่น

UPDATE
DELETE

ตาม Query ที่ใช้

สิ่งสำคัญคือเลือก Method ตาม ผลลัพธ์ที่ต้องการ ไม่ใช่เลือก query กับทุกคำสั่งโดยอัตโนมัติ

㉘ MySQL.prepare คืออะไร

prepare ใช้ Prepared Statement Execution

ตัวอย่าง

local result = MySQL.prepare.await(
    'SELECT `username` FROM `users` WHERE `id` = ?',
    { userId }
)

และสามารถทำ Query เดียวกับ Parameter Sets หลายชุดตาม API

เหมาะกับ Query ที่ถูกเรียกซ้ำและต้องการรูปแบบ Parameters ที่ชัดเจน

㉙ prepare ดีกว่า query เสมอไหม

ไม่ควรสรุปแบบนั้น

ต้องเลือกตาม Use Case

query
→ General Query

single
→ Row เดียว

scalar
→ ค่าเดียว

insert
→ Insert ID

update
→ Affected Rows

prepare
→ Prepared execution

transaction
→ หลาย Query ที่ต้องสำเร็จร่วมกัน

การเลือก Function ที่ตรงกับข้อมูลที่ต้องการทำให้ Code อ่านง่ายกว่า

㉚ MySQL.transaction คืออะไร

Transaction ใช้เมื่อหลาย SQL Statements ต้องถือเป็น งานชุดเดียวกัน

หลักคือ

ทุก Query สำเร็จ
→ COMMIT

มี Query ใด Query หนึ่งล้มเหลว
→ ไม่ Commit ทั้งชุด

Current Documentation ระบุชัดว่า หาก Query หนึ่ง Fail จะไม่มี Query ใน Transaction นั้นถูก Commit

㉛ Transaction สำคัญกับระบบเงินอย่างไร

สมมติโอนเงิน

Player A
↓ -10,000

Player B
↑ +10,000

ถ้าทำเป็นสอง Query แยกกัน

หัก Player A สำเร็จ
↓
Server/Query B Error
↓
Player B ไม่ได้เงิน

เงินหายจาก Economy

Transaction ช่วยให้

หัก A
+
เพิ่ม B

เป็นงานชุดเดียว

ถ้าขั้นตอนใดล้มเหลวทั้งหมดสามารถไม่ Commit

㉜ ตัวอย่าง Transaction

local queries = {
    {
        'UPDATE accounts SET balance = balance - ? WHERE id = ?',
        { amount, fromAccount }
    },
    {
        'UPDATE accounts SET balance = balance + ? WHERE id = ?',
        { amount, toAccount }
    }
}

local success = MySQL.transaction.await(queries)

if not success then
    print('Transaction failed')
end

นี่เหมาะกับ Banking/Economy Logic

แต่ Server ต้องตรวจว่า Sender มีเงินเพียงพอก่อนและออกแบบ Query ให้ป้องกัน Race Conditions ตามระบบด้วย

㉝ Transaction ใช้กับอะไรอีก

เหมาะกับ

ซื้อรถ
ซื้อบ้าน
ซื้อธุรกิจ
Trade
Bank Transfer
Marketplace
Crafting ที่เกี่ยวหลาย Tables
Character Creation หลาย Tables

โดยเฉพาะ Action ที่ข้อมูลหลายจุดต้องสอดคล้องกันทั้งหมด

㉞ Transaction Isolation Level ปรับได้ไหม

ได้

Current Documentation มี Convar

mysql_transaction_isolation_level

ค่าอยู่ระหว่าง

1–4

และ Current Default ระบุเป็น

2 = Read Committed

การเปลี่ยน Isolation Level ควรทำเฉพาะเมื่อเข้าใจ Database Transactions จริง เพราะมีผลต่อ Concurrency และ Locking

㉟ Callback กับ await ต่างกันอย่างไร

oxmysql รองรับทั้งสองรูปแบบ

Callback

MySQL.query(
    'SELECT * FROM users',
    {},
    function(result)
        print(#result)
    end
)

await

local result = MySQL.query.await(
    'SELECT * FROM users'
)

print(#result)

await มักทำให้ Flow ของ Lua Code อ่านง่ายขึ้น

㊱ await ทำให้ Server ทั้งเครื่องหยุดหรือไม่

ไม่ควรเข้าใจคำว่า await ว่าเป็นการ Freeze FXServer ทั้งหมด

oxmysql รองรับ Promise/Async Query Handling และ Lua Coroutine-style awaiting

ดังนั้น Code สามารถรอผล Query ใน Execution Context นั้นได้โดยไม่หมายความว่า Database Query เดียวหยุดทุก Resource ทั้ง Server

อย่างไรก็ตาม Query ที่ช้าและ Query จำนวนมากยังสร้างภาระให้ Database/Server ได้จริง

㊲ Async ไม่ได้แปลว่า Query ช้าไม่มีผล

ถึงระบบจะจัดการ Query แบบ Async ได้

ถ้ามี Script ทำ

10,000 Queries
ทุกไม่กี่วินาที

Database ก็ยังทำงานหนัก

Performance ต้องพิจารณา

จำนวน Queries
Indexes
Query Design
Rows ที่อ่าน
Connection Pool
Database Hardware

ไม่ใช่เพียงเลือก await หรือ Callback

㊳ SELECT * ควรใช้ทุกครั้งไหม

ไม่ควร

ถ้าต้องการเพียง

firstname
lastname

ควร Query

SELECT firstname, lastname
FROM users
WHERE identifier = ?

แทน

SELECT *
FROM users

โดยเฉพาะ Tables ใหญ่

ช่วยลดข้อมูลที่ Database ต้องส่งกลับ

㊴ LIMIT 1 มีประโยชน์เมื่อไร

ถ้าค้น Record เดียว เช่น

identifier
citizenid
vehicle id

สามารถใช้

LIMIT 1

เมื่อ Logic และ Schema เหมาะสม

ตัวอย่าง

MySQL.single.await(
    'SELECT `firstname` FROM `users` WHERE `identifier` = ? LIMIT 1',
    { identifier }
)

ช่วยสื่อ Intent ว่าต้องการเพียง Record เดียว

㊵ Database Index สำคัญกว่าเปลี่ยน Library บางครั้ง

ถ้า Query

SELECT *
FROM vehicles
WHERE owner = ?

ถูกเรียกบ่อยมาก แต่ Column owner ไม่มี Index ที่เหมาะสม

Database อาจต้อง Scan Rows จำนวนมาก

ดังนั้น Performance Problem อาจไม่ได้เกิดจาก oxmysql

แต่อยู่ที่

SQL Query
Schema
Indexes
Data Volume

ต้องใช้ Database Profiling/Slow Query Analysis ประกอบ

㊶ oxmysql มี Debug Mode หรือไม่

มี

Current Documentation รองรับ

set mysql_debug true

เพื่อ Debug Queries

และสามารถจำกัด Resource ที่ต้องการ Debug ได้

ตัวอย่างแนวคิด

set mysql_debug [
    "ox_core",
    "ox_inventory"
]

เหมาะกับการหา Script ที่ส่ง Query ผิดหรือช้า

㊷ เปิด mysql_debug ตลอด Production ดีไหม

ไม่จำเป็น

Debug Logging จำนวนมากสามารถสร้าง Log ปริมาณสูง

ควรเปิดเมื่อ

วิเคราะห์ Query
หา Error
หา Resource ที่ Query ผิด

แล้วปิดหรือจำกัด Scope เมื่อแก้ปัญหาแล้ว

Production Logging ควรสมดุลระหว่าง Visibility กับ Noise

㊸ Connection String คืออะไร

oxmysql ต้องรู้ว่าจะเชื่อม Database ไหน

ใช้ Convar

mysql_connection_string

ตัวอย่างรูปแบบ URI

set mysql_connection_string "mysql://fivem:StrongPassword@127.0.0.1/fivem"

ประกอบด้วย

Protocol
Username
Password
Host
Database

Port สามารถระบุเพิ่มได้ถ้าไม่ใช้ Default

㊹ Connection String ต้องอยู่ก่อน ensure oxmysql

ควรตั้ง

set mysql_connection_string "..."

ก่อน

ensure oxmysql

เพราะ oxmysql ต้องอ่าน Connection Configuration ตอนเริ่มเชื่อม Database

ตัวอย่าง

set mysql_connection_string "mysql://fivem:password@127.0.0.1/fivem"

ensure oxmysql

㊺ อย่าเปิดเผย Database Password

ไม่ควรโพสต์ Connection String จริงลง

Discord
Forum
GitHub Public Repository
Screenshot สาธารณะ

เพราะภายในมี

Username
Password
Hostname
Database

ถ้า Credential หลุดควรเปลี่ยน Password ทันที

㊻ ควรใช้ root หรือไม่

Production Server ควรสร้าง Database User เฉพาะสำหรับ FiveM

เช่น

Database:
fivem

User:
fivem_server

แทนการให้ FXServer ใช้ Database Administrator Account โดยไม่จำเป็น

หลักคือให้สิทธิ์เท่าที่ระบบต้องใช้

㊼ Connection refused คืออะไร

ถ้าเจอ

Connection refused

ให้ตรวจ

MariaDB เปิดอยู่ไหม
Host ถูกไหม
Port ถูกไหม
Firewall
Bind Address
Network

Error นี้ไม่ได้หมายความว่า SQL ของ Script ผิด

Connection ยังไปไม่ถึงขั้น Query

㊽ Access denied คืออะไร

ตัวอย่าง

Access denied for user

มักเกี่ยวกับ

Username
Password
Host permissions
Database privileges

ตรวจ Database Account ก่อน

อย่าแก้ oxmysql Source เพื่อแก้ Authentication Error

㊾ Unknown database คืออะไร

ถ้า Connection String ระบุ

fivem

แต่ Database จริงไม่มี

อาจเห็น

Unknown database

ให้สร้าง Database หรือแก้ชื่อใน Connection String

ไม่เกี่ยวกับ Player Cache

㊿ Unknown table คืออะไร

ถ้า Query ขอ

users

แต่ Table ไม่มี

Database จะ Error

สาเหตุอาจเป็น

ยังไม่ได้ Import Framework SQL
ใช้ Database ผิดตัว
Framework Bridge ผิด
Schema Version ไม่ตรง

ต้องตรวจ SQL Installation ของ Framework/Resource นั้น

51. Unknown column คืออะไร

เช่น

Unknown column 'citizenid'

อาจเกิดจาก

Schema เก่า
Framework ผิด
Migration ไม่ครบ
Query ใช้ Column คนละระบบ

เช่น Qbox/QBCore และ ESX มี Database Schemas ต่างกัน

อย่าเพิ่ม Column แบบเดาโดยไม่ตรวจ Current Schema

52. oxmysql ใช้แทน mysql-async ได้ไหม

Current Documentation ระบุ Compatibility สำหรับ

mysql-async
ghmattimysql

และ oxmysql สามารถ Provide Functionality สำหรับ Resource Names เหล่านี้ได้

Current README ยังระบุ Support สำหรับ Syntax ของทั้งสองระบบ

นี่ช่วยให้ Resources เก่าหลายตัว Migration ได้ง่ายขึ้น

53. ต้องเปิด mysql-async พร้อม oxmysql ไหม

Current Documentation ระบุว่าสามารถลบ

mysql-async
ghmattimysql

แล้วให้ oxmysql Provide Compatibility ได้

ดังนั้นโดยทั่วไปไม่ควรรัน Database Wrapper หลายตัวซ้อนโดยไม่มีเหตุผล

ควรมี Database Resource หลักที่ชัดเจน

54. Compatibility หมายความว่า Script เก่าทุกตัวทำงาน 100% หรือไม่

ไม่ควรสรุปแบบนั้น

แม้ oxmysql รองรับ Syntax/Compatibility จำนวนมาก แต่ Script เก่ามากอาจมี

API Assumptions
Deprecated Calls
SQL Syntax เก่า
MySQL-version-specific behavior

ที่ยังต้องแก้

ดังนั้น Migration ต้องทดสอบ Resource จริง

55. mysql-async Syntax รุ่นเก่ายังพบได้

เช่น

MySQL.Async.fetchAll(...)

หรือ

MySQL.Sync.fetchAll(...)

oxmysql มี Compatibility Aliases สำหรับ Calls หลายแบบ

แต่สำหรับ Script ใหม่ควรใช้ Current API ที่อ่านชัดกว่า เช่น

MySQL.query.await(...)

56. ทำไมไม่ควรใช้ Sync เป็นคำอธิบายแบบเก่า

คำว่า

Sync

ใน APIs รุ่นเก่าอาจทำให้เข้าใจว่าทำ Blocking Database Call แบบดั้งเดิม

แต่ Current Lua API ใช้

.await

เพื่อสื่อ Async/Promise/Coroutine Flow ได้ชัดกว่า

สำหรับ Code ใหม่จึงนิยมรูปแบบ

MySQL.query.await(...)

มากกว่า Legacy Aliases

57. oxmysql รองรับ JavaScript หรือไม่

รองรับ

สามารถใช้ Raw Exports หรือ Package

@overextended/oxmysql

สำหรับ JavaScript/TypeScript

ตัวอย่างแนวคิด

const result = await MySQL.query(
  'SELECT * FROM users WHERE id = ?',
  [id]
)

ดังนั้น oxmysql ไม่ได้จำกัดเฉพาะ Lua Scripts

58. Upsert คืออะไร

บางระบบต้องการ

ถ้ามี Record
→ Update

ถ้ายังไม่มี
→ Insert

Current Documentation แนะนำใช้ Database Constraint และ

INSERT ... ON DUPLICATE KEY UPDATE

ใน Use Case ที่เหมาะสม

แทน

SELECT ก่อน
↓
ถ้ามี UPDATE
ถ้าไม่มี INSERT

ซึ่งสร้าง Queries เพิ่มโดยไม่จำเป็น

59. ทำไมไม่ควรใช้ REPLACE INTO แบบสุ่ม

Current Documentation ชี้ว่า ON DUPLICATE KEY UPDATE มีข้อดีตรงที่ไม่ต้อง

DELETE ROW
↓
INSERT ROW ใหม่

แบบ Behavior ของ REPLACE INTO

เรื่องนี้สำคัญเมื่อ Record มี

Foreign Keys
IDs
Relations

ที่ไม่ควรถูกลบแล้วสร้างใหม่โดยไม่ตั้งใจ

60. ทำไม oxmysql ถึงสำคัญกับ Economy Server

Economy Server มี Transactions จำนวนมาก

เช่น

เงินสด
Bank
Shops
Businesses
Vehicles
Houses
Inventory
Crafting
Market

ทั้งหมดต้องมี Data Consistency

oxmysql ให้ APIs เช่น

query
single
scalar
insert
update
prepare
transaction

ทำให้ Scripts เข้าถึง Database ผ่าน Interface กลาง

แต่ความถูกต้องของ Economy ยังขึ้นกับ SQL/Server Logic ที่ Developer เขียนด้วย

oxmysql ไม่ได้ทำให้ SQL ดีอัตโนมัติ

ถึงใช้ oxmysql

Query แบบนี้ก็ยังไม่ดี

Query ทุก Frame
SELECT * ทั้ง Table
ไม่มี Index
โหลดข้อมูลหลายหมื่น Row โดยไม่จำเป็น

Database Resource ไม่สามารถแก้ Query Design ที่ผิดได้ทั้งหมด

Developer ยังต้องเข้าใจ

SQL
Indexes
Transactions
Schema
Caching

พื้นฐาน

อย่า Query Database ทุก Frame

ไม่ควรมี

while true do
    Wait(0)

    MySQL.query.await(...)
end

เพื่อเช็กข้อมูล Player ทุก Frame

Database เหมาะกับ Persistent Data ไม่ใช่เป็น Replacement ของ Runtime Memory

ควรโหลดข้อมูลที่จำเป็นแล้ว Cache/เก็บ State ตาม Architecture ของ Framework

Database กับ Runtime State ต่างกันอย่างไร

ตัวอย่าง

Player Online
↓
Framework Player Object
↓
Runtime State

ส่วน Database

Persistent Storage

Framework มักเก็บ Player Data ที่ใช้งานบ่อยไว้ใน Memory แล้ว Save ลง Databaseตามจังหวะ

ไม่ควร Query DB ทุกครั้งที่ต้องดู Job หรือยอดเงินหาก Framework มี API สำหรับข้อมูลนั้นอยู่แล้ว

อย่า Direct SQL แก้ Framework Data ขณะ Player Online โดยไม่จำเป็น

เช่น

UPDATE players
SET job = 'police'
WHERE citizenid = ...

Database อาจเปลี่ยน แต่ Framework Runtime State ยังเป็น Job เดิม

จึงเกิด

Database = police
Runtime = unemployed

ควรใช้ Framework API สำหรับ Gameplay State แล้วให้ Framework จัด Persistence

Direct SQL เหมาะกับงานที่ Architecture ออกแบบไว้จริง

oxmysql กับ ox_inventory เกี่ยวกันอย่างไร

ox_inventory ใช้ oxmysql เป็น Dependency หลัก

Architecture

ox_inventory
↓
oxmysql
↓
Database

เพื่อบันทึก Persistent Data เช่น Stashes และข้อมูลที่เชื่อมกับ Framework/Vehicle Storage

ถ้า oxmysql ใช้งานไม่ได้ Inventory Persistence ก็ได้รับผลกระทบ

oxmysql กับ ox_lib ต่างกันอย่างไร

oxmysql

Database Interface

ox_lib

Library / UI / Utilities

สอง Resource ไม่ได้ทำหน้าที่แทนกัน

Server อาจใช้ทั้งคู่ เช่น

oxmysql
+
ox_lib
+
qbx_core
+
ox_inventory

oxmysql กับ Framework ต่างกันอย่างไร

Framework คือ

Player
Character
Job
Money
Groups
Gameplay APIs

oxmysql คือ

SQL Communication Layer

ตัวอย่าง

Qbox
↓
oxmysql
↓
MariaDB

อย่าสั่ง oxmysql ให้ SetJob โดยตรงเมื่อ Framework มี API สำหรับ SetJob

Server ควร Start oxmysql ตอนไหน

ควร Start ก่อน Resources ที่ต้องใช้ Database

ตัวอย่าง

set mysql_connection_string "mysql://fivem:password@127.0.0.1/fivem"

ensure oxmysql
ensure ox_lib
ensure qbx_core
ensure ox_target
ensure ox_inventory

หรือ ESX

ensure oxmysql
ensure es_extended

ตาม Dependency ของ Stack จริง

ถ้า oxmysql ไม่ Start จะเกิดอะไร

Framework/Resources ชั้นบนอาจเจอ

Missing dependency
Database not ready
Query error
Player data ไม่โหลด
Character เข้าไม่ได้
Inventory ไม่ Save
Vehicle ไม่ Save

ดังนั้นหาก Server Error หลายระบบพร้อมกัน ควรตรวจ Database Layer ก่อน

Checklist ตรวจ oxmysql

Database

  • MariaDB ทำงาน

  • Database มีอยู่

  • DB User ถูก

  • Password ถูก

  • Permissions ถูก

Connection

  • mysql_connection_string ถูก

  • Host ถูก

  • Port ถูก

  • Database Name ถูก

Resource

  • Folder ชื่อ oxmysql

  • ใช้ Current Release

  • Resource Start สำเร็จ

Start Order

oxmysql
↓
Framework/Resources ที่ต้องใช้ DB

SQL

  • Tables มี

  • Columns ตรง

  • Schema Version ตรง

  • Parameters ถูก

Performance

  • ไม่มี Query Loop หนัก

  • มี Index ที่เหมาะสม

  • ไม่ SELECT ข้อมูลเกินจำเป็น

ตาราง Functions สำคัญของ oxmysql

Functionเหมาะกับ
MySQL.queryดึง Rows หรือ Query ทั่วไป
MySQL.singleดึง Row เดียว
MySQL.scalarดึงค่าเดียว
MySQL.insertInsert และรับ Insert ID
MySQL.updateUpdate/Delete และรับจำนวน Rows ที่กระทบ
MySQL.preparePrepared execution
MySQL.transactionหลาย Queries ที่ต้องสำเร็จร่วมกัน

เลือกให้ตรงกับ Intent ของ Query จะทำให้ Code อ่านง่ายกว่า

ตัวอย่าง Resource ใช้ oxmysql แบบพื้นฐาน

fxmanifest.lua

fx_version 'cerulean'
game 'gta5'

server_script '@oxmysql/lib/MySQL.lua'
server_script 'server.lua'

dependency 'oxmysql'

server.lua

RegisterCommand('dbtest', function(source)
    local row = MySQL.single.await(
        'SELECT ? AS message',
        { 'oxmysql ทำงานแล้ว' }
    )

    if row then
        print(row.message)
    end
end, true)

เหมาะสำหรับทดสอบว่า Lua Resource มองเห็น MySQL Interface แล้ว

ตัวอย่างค้นหา Character

local character = MySQL.single.await(
    'SELECT `firstname`, `lastname` FROM `characters` WHERE `id` = ? LIMIT 1',
    { characterId }
)

if not character then
    return
end

print(character.firstname, character.lastname)

ใช้ Parameters แยกออกจาก Query String

ตัวอย่างนับรถ

local vehicleCount = MySQL.scalar.await(
    'SELECT COUNT(*) FROM `vehicles` WHERE `owner` = ?',
    { owner }
)

print(vehicleCount)

ถ้าต้องการแค่จำนวน scalar เหมาะกว่าดึง Rows ทั้งหมดมา Count ใน Lua

ตัวอย่าง Insert

local id = MySQL.insert.await(
    'INSERT INTO `vehicles` (`owner`, `plate`) VALUES (?, ?)',
    {
        owner,
        plate
    }
)

if not id then
    return
end

print(('Vehicle ID: %s'):format(id))

ตัวอย่าง Update

local affected = MySQL.update.await(
    'UPDATE `vehicles` SET `stored` = ? WHERE `id` = ?',
    {
        true,
        vehicleId
    }
)

if affected == 0 then
    print('Vehicle not found')
end

ทำให้ตรวจได้ว่า Query แก้ Record จริงหรือไม่

ตัวอย่าง Transaction สำหรับซื้อรถ

แนวคิด

ตรวจ Player
↓
ตรวจราคา
↓
Transaction
├── หักเงิน
└── สร้างรถ
↓
Success

ช่วยให้ข้อมูลสองส่วนสัมพันธ์กัน

แต่ในระบบเงินจริงควรออกแบบเงื่อนไข SQL ให้ป้องกันยอดติดลบและ Concurrent Transactions อย่างถูกต้องด้วย

ข้อผิดพลาดที่พบบ่อย

Access denied

ตรวจ

Username
Password
Privileges

Connection refused

ตรวจ

MariaDB
Host
Port
Firewall

Unknown database

ตรวจชื่อ Database

Unknown table

Import SQL/Schema ถูกหรือไม่

Unknown column

Framework/Schema Version ตรงหรือไม่

MySQL is nil

ตรวจ

server_script '@oxmysql/lib/MySQL.lua'

และ Start Order

Query ช้า

ตรวจ

SQL
Indexes
Rows
Database Performance

ไม่ใช่ Reinstall oxmysql เป็นอย่างแรก

oxmysql เหมาะกับ Production หรือไม่

เหมาะและถูกใช้อย่างแพร่หลายใน FiveM Frameworks ปัจจุบัน

แต่ Production Setup ควรมี

Dedicated MariaDB
Strong DB Password
Database User แยก
Firewall
Backups
Indexes
Monitoring
Test Server

ไม่ใช่เพียงติดตั้ง Resource แล้วถือว่า Database Infrastructure เสร็จ

Database Backup สำคัญแค่ไหน

สำคัญมาก

เพราะ Database เก็บข้อมูลที่ผู้เล่นใช้เวลาสะสมเป็นเดือนหรือปี

เช่น

Character
Money
Vehicles
Houses
Businesses
Inventory
Phone

ควรมี Automated Backup และทดสอบ Restore ด้วย

Backup ที่ Restore ไม่ได้ไม่ใช่ Backup ที่เชื่อถือได้

สรุป oxmysql คืออะไร ทำไม FiveM ต้องใช้

oxmysql คือ FiveM/FXServer Resource สำหรับสื่อสารกับ MySQL-compatible Database โดยใช้ node-mysql2

มันทำหน้าที่เป็น Layer

FiveM Resource
↓
oxmysql
↓
MariaDB / MySQL

ไม่ใช่ Database Server เอง

Current Documentation แนะนำ MariaDB สำหรับ FiveM และไม่แนะนำให้ติดตั้ง XAMPP เพียงเพื่อใช้ Database Server

สำหรับ Lua Resource สามารถ Import ด้วย

server_script '@oxmysql/lib/MySQL.lua'

แล้วใช้ APIs สำคัญ เช่น

MySQL.query
MySQL.single
MySQL.scalar
MySQL.insert
MySQL.update
MySQL.prepare
MySQL.transaction

โดยรูปแบบ .await ทำให้เขียน Async Database Logic ใน Lua ได้อ่านง่ายขึ้น

oxmysql ยังมี Compatibility กับ mysql-async และ ghmattimysql ซึ่งช่วยในการใช้งาน Resources รุ่นเก่า แต่สำหรับ Code ใหม่ควรใช้ Current APIs และ Parameterized Queries มากกว่า Legacy Syntax

สิ่งสำคัญคือ FiveM ไม่ได้ “ต้องใช้ oxmysql” ทุก Server หากไม่มี Persistent Database แต่ Server RP ปัจจุบันที่ใช้ ESX, QBCore, Qbox, ox_core, Inventory, Phone, Housing หรือ Economy Systems จำนวนมากมักต้องมี Database Layer และ Framework เหล่านี้จำนวนมากเลือกใช้ oxmysql

แนวทางของ comsiam คือให้มอง oxmysql เป็น Infrastructure Layer ไม่ใช่ Gameplay Script ถ้า Player เข้าไม่ได้, Inventory ไม่ Save, รถหาย หรือ Character โหลดไม่ได้ และหลายระบบมี SQL Error พร้อมกัน ให้ตรวจ MariaDB → Connection String → oxmysql ก่อนไล่แก้ Framework ชั้นบน

สำหรับ Production comsiam แนะนำให้ให้ความสำคัญกับ SQL Design พอ ๆ กับตัว oxmysql เอง เพราะ Database Resource ที่ดีไม่สามารถแก้ SELECT * จำนวนมหาศาล, Query ทุก Frame, Missing Index หรือ Transaction Logic ที่ผิดได้ การใช้ Parameters, Indexes, Transactions และ Framework APIs ให้ถูกจึงเป็นหัวใจของ FiveM Server ที่เสถียรในระยะยาว

Comments

Popular posts from this blog

FiveM ยังน่าเล่นไหม? Enhanced เปลี่ยน FiveM แค่ไหน

FiveM คืออะไร เล่นอย่างไร สำหรับมือใหม่ เริ่มต้นตั้งแต่ศูนย์

วิธีตั้ง Admin Permission ด้วย add_ace และ add_principal FiveM แบบละเอียด