oxmysql FiveM คืออะไร ทำไม Script จำนวนมากถึงใช้
oxmysql คือ Resource สำหรับเชื่อม FXServer ของ FiveM เข้ากับฐานข้อมูล MySQL หรือ MariaDB ทำให้ Server Scripts สามารถอ่าน เพิ่ม แก้ไข และลบข้อมูลใน Database ผ่าน API ที่ออกแบบมาสำหรับ FiveM เช่น query, single, scalar, insert, update, prepare และ transaction
โครงสร้างโดยรวมคือ
FiveM Client
↓
Server Resource
↓
oxmysql
↓
MySQL / MariaDB
↓
Database
ดังนั้นต้องเข้าใจก่อนว่า
oxmysql
≠ Database
ตัว Database จริงคือ
MySQL
หรือ
MariaDB
ส่วน oxmysql เป็นตัวกลางระหว่าง
FiveM Server Script
↕
Database Server
เหตุผลที่ Script FiveM จำนวนมากใช้ oxmysql คือมี API ครอบคลุม, รองรับ Lua/JavaScript, มีรูปแบบ .await, Parameterized Queries, Transactions, Prepared Queries, Slow Query Warnings และมี Compatibility สำหรับ Resource รุ่นเก่าที่เคยใช้ mysql-async หรือ ghmattimysql
① oxmysql คืออะไร
ชื่อ oxmysql มาจาก Database Resource ในกลุ่ม Overextended
หน้าที่หลักคือ
FXServer
↓
ส่ง SQL Query
↓
oxmysql
↓
MySQL/MariaDB
↓
รับ Result
↓
ส่งกลับ Server Script
ตัวอย่าง
local rows =
MySQL.query.await(
'SELECT * FROM users'
)
Server Script ไม่ต้องจัดการ Network Protocol ของ MySQL เอง
oxmysql จัดการ Connection และ Database Communication ให้
② oxmysql ใช้ทำอะไรใน FiveM
ใช้กับระบบที่ต้องเก็บข้อมูลถาวร เช่น
Characters
Money
Bank
Inventory
Vehicles
Garages
Houses
Jobs
Businesses
Phone
Licenses
Punishments
Logs
ตัวอย่าง Garage ต้องหา Vehicle ของ Character
local vehicles =
MySQL.query.await(
[[
SELECT *
FROM vehicles
WHERE character_id = ?
]],
{
characterId
}
)
ผลลัพธ์ถูกส่งกลับมาเป็น Lua Table
③ oxmysql เป็นฐานข้อมูลไหม
ไม่
นี่เป็นสิ่งที่มือใหม่ควรจำให้ชัด
MariaDB / MySQL
→ Database Server
oxmysql
→ Database Connector
ถ้าติดตั้ง oxmysql แต่ไม่มี Database Server ให้เชื่อมต่อ
ก็ไม่สามารถเก็บข้อมูลได้ตามปกติ
④ oxmysql เชื่อมกับ Database อย่างไร
Server กำหนด Connection String
ตัวอย่าง
set mysql_connection_string "mysql://username:password@localhost:3306/fivem"
จากนั้น Start oxmysql
ensure oxmysql
แล้ว Resources ที่ต้องใช้ Database จึง Start ตามมา
ensure my_core
ensure my_inventory
ensure my_garage
⑤ Connection String บอกอะไร
ตัวอย่าง
mysql://username:password@localhost:3306/fivem
ประกอบด้วย
username
password
host
port
database
Concept คือ
FXServer
↓
ใช้ Credentials
↓
เชื่อม Database Server
↓
เลือก Database fivem
รายละเอียดการตั้งค่าจะลงเต็มในบทความ 453
⑥ ทำไม oxmysql ต้อง Start ก่อน Script Database
สมมติ Garage ใช้
MySQL.query.await(...)
แต่ oxmysql ยังไม่พร้อม
Database API จะใช้ไม่ได้ตามที่ Resource คาดหวัง
ดังนั้น Startup Order ควรเป็น
Database Server
↓
oxmysql
↓
Framework/Core
↓
Gameplay Resources
ตัวอย่าง
ensure oxmysql
ensure my_core
ensure my_garage
⑦ Resource ควรประกาศ dependency ไหม
ถ้า Resource จำเป็นต้องใช้ oxmysql จริง สามารถระบุ
dependency 'oxmysql'
ใน fxmanifest.lua
ตัวอย่าง
fx_version 'cerulean'
game 'gta5'
dependency 'oxmysql'
server_script 'server.lua'
ทำให้ Requirement ของ Resource ชัดเจนขึ้น
⑧ วิธี Import oxmysql ใน Lua
Documentation ปัจจุบันรองรับ
server_script '@oxmysql/lib/MySQL.lua'
ใน fxmanifest.lua
และควรวาง Library ก่อน Server Scripts ที่เรียก MySQL
ตัวอย่าง
fx_version 'cerulean'
game 'gta5'
server_script '@oxmysql/lib/MySQL.lua'
server_scripts {
'server/database.lua',
'server/main.lua'
}
⑨ ทำไมต้องวาง MySQL.lua ก่อน Scripts อื่น
เพราะ Files ที่ตามมาจะสามารถใช้ Global API
MySQL
เช่น
MySQL.query.await(...)
ได้
ถ้า server/database.lua ถูกโหลดก่อน Library ที่ Resource Architecture พึ่งอยู่ ก็อาจเกิดปัญหาตอนเรียก API
⑩ oxmysql ใช้ Export ตรง ๆ ได้ไหม
มี APIs ผ่าน Exports โดยเฉพาะ JavaScript และ Compatibility Cases
แต่สำหรับ Lua Documentation แนะนำให้ Import
@oxmysql/lib/MySQL.lua
เพราะให้ Syntax อย่าง
MySQL.query.await(...)
ที่อ่านง่ายและมีประโยชน์ด้าน Debug/Type Handling เพิ่มเติมตาม Library
⑪ API หลักของ oxmysql มีอะไร
กลุ่มที่ Developer FiveM ควรรู้คือ
MySQL.query
MySQL.single
MySQL.scalar
MySQL.insert
MySQL.update
MySQL.prepare
MySQL.transaction
แต่ละตัวเหมาะกับ Result Type คนละแบบ
ไม่จำเป็นต้องใช้ query กับทุกกรณี
⑫ MySQL.query คืออะไร
ใช้เมื่อ Query อาจคืนหลาย Rows
ตัวอย่าง
local vehicles =
MySQL.query.await(
[[
SELECT id, plate, model
FROM vehicles
WHERE character_id = ?
]],
{
characterId
}
)
ผลลัพธ์ประมาณ
{
{
id = 10,
plate = 'ABC123',
model = 'sultan'
},
{
id = 11,
plate = 'XYZ456',
model = 'blista'
}
}
⑬ query เหมาะกับอะไร
เช่น
รายชื่อรถทั้งหมด
รายการสินค้า
Transaction History
Player Logs
Inventory Rows
House List
หากคาดว่าจะได้หลาย Records
query เหมาะกว่า single
⑭ MySQL.single คืออะไร
ใช้เมื่อคาดว่าจะต้องการเพียง Row เดียว
ตัวอย่าง
local player =
MySQL.single.await(
[[
SELECT id, cash, bank
FROM characters
WHERE id = ?
LIMIT 1
]],
{
characterId
}
)
แล้ว
if not player then
return
end
print(
player.cash
)
⑮ single ต่างจาก query อย่างไร
query
คืนหลาย Rows
→ Array/Table of rows
single
คืน Row เดียว
→ Row table
หากต้องการ User คนเดียว single ทำ Intent ของ Code ชัดกว่า
⑯ MySQL.scalar คืออะไร
ใช้เมื่อเราต้องการเพียง Column แรกของ Row เดียว
เช่นหาเงิน
local cash =
MySQL.scalar.await(
[[
SELECT cash
FROM characters
WHERE id = ?
LIMIT 1
]],
{
characterId
}
)
ผลคือ
5000
ไม่ใช่ Row Table ทั้งชุด
⑰ scalar เหมาะกับอะไร
เช่น
COUNT(*)
cash
bank
name
boolean-like state
single aggregate value
ตัวอย่าง
local count =
MySQL.scalar.await(
[[
SELECT COUNT(*)
FROM vehicles
WHERE character_id = ?
]],
{
characterId
}
)
⑱ MySQL.insert คืออะไร
ใช้กับ INSERT
และคืน Insert ID ในกรณีที่ Database Query มี Auto Increment ID
ตัวอย่าง
local vehicleId =
MySQL.insert.await(
[[
INSERT INTO vehicles (
character_id,
plate,
model
)
VALUES (?, ?, ?)
]],
{
characterId,
plate,
model
}
)
จากนั้น
print(
'New vehicle ID:',
vehicleId
)
⑲ insert เหมาะกับอะไร
เช่นสร้าง
Character
Vehicle
Property
Invoice
Business
Log record
ที่ต้องการ Row ID ใหม่กลับมา
⑳ MySQL.update คืออะไร
ใช้กับ Query ที่แก้ข้อมูล และคืนจำนวน Rows ที่ได้รับผล
ตัวอย่าง
local affectedRows =
MySQL.update.await(
[[
UPDATE vehicles
SET stored = ?
WHERE id = ?
]],
{
1,
vehicleId
}
)
สามารถตรวจ
if affectedRows < 1 then
return
end
เพื่อดูว่ามี Record ถูกเปลี่ยนจริงหรือไม่
㉑ update ใช้กับ DELETE ได้ไหม
API ชื่อ update เน้น Result แบบจำนวน Rows ที่ได้รับผล
Query ประเภท Update/Delete บางกรณีสามารถใช้ API ที่คืน Affected Rows ตามรูปแบบที่เหมาะสม
แต่ Code ควรเลือก Method ให้ Intent อ่านเข้าใจง่าย
㉒ MySQL.prepare คืออะไร
prepare ใช้กับ Prepared Query Pattern
ตัวอย่าง
local player =
MySQL.prepare.await(
[[
SELECT id, cash
FROM characters
WHERE id = ?
]],
{
characterId
}
)
Return Type จะขึ้นกับชนิด SQL Query
Prepared Queries เหมาะโดยเฉพาะกับ Query ที่ใช้ซ้ำบ่อย ๆ
㉓ prepare ทำให้ทุก Query เร็วขึ้นเสมอไหม
ไม่ควรคิดแบบนั้น
Performance ขึ้นกับ
Query Design
Indexes
จำนวน Rows
Database Load
Network
Execution Frequency
Prepared Query เป็นหนึ่งในเครื่องมือ
ไม่ใช่ปุ่ม
เปิดแล้ว Database เร็วทันที
㉔ MySQL.transaction คืออะไร
ใช้จัดหลาย Queries ให้เป็น Transaction เดียว
Concept
Query A
+
Query B
+
Query C
ทุกตัวสำเร็จ
→ Commit
มีตัวใดล้มเหลว
→ Rollback
เหมาะกับข้อมูลที่ต้อง Consistent กัน
㉕ ตัวอย่าง Transaction โอนเงิน
ต้องการ
Account A
- 1000
Account B
+ 1000
ไม่ควรหัก A สำเร็จแต่เพิ่ม B ล้มเหลว
ใช้ Transaction
local success =
MySQL.transaction.await({
{
[[
UPDATE accounts
SET balance =
balance - ?
WHERE id = ?
]],
{
amount,
senderId
}
},
{
[[
UPDATE accounts
SET balance =
balance + ?
WHERE id = ?
]],
{
amount,
receiverId
}
}
})
㉖ Transaction ใช้กับอะไรบ้าง
เหมาะกับ
Transfer Money
ซื้อรถ
ซื้อบ้าน
โอน Ownership
Craft Item
ซื้อสินค้า + ตัดเงิน
Business Payment
เมื่อมีหลาย Database Changes ที่ควรสำเร็จพร้อมกัน
㉗ .await คืออะไร
ตัวอย่าง
local row =
MySQL.single.await(
query,
params
)
หมายถึง Lua Coroutine จะรอ Result ก่อนดำเนิน Code ต่อ
ทำให้เขียน Code เป็นลำดับได้ง่าย
Query
↓
ได้ Result
↓
Validate
↓
ทำงานต่อ
โดยไม่ต้องเขียน Callback ซ้อนหลายชั้น
㉘ .await เท่ากับ Blocking FXServer ทั้งเครื่องไหม
ไม่ควรตีความแบบนั้น
await ทำงานร่วมกับ Citizen coroutine/scheduler
คือ Coroutine ปัจจุบันรอ Promise/Result
ไม่ใช่การ Freeze FXServer Process แบบ Blocking Loop ที่ไม่ Yield
แต่ Query จำนวนมากหรือ Query ช้ายังสร้าง Load และ Latency ได้
㉙ oxmysql รองรับ Callback ไหม
รองรับ
ตัวอย่าง
MySQL.single(
[[
SELECT id, cash
FROM characters
WHERE id = ?
LIMIT 1
]],
{
characterId
},
function(player)
if not player then
return
end
print(
player.cash
)
end
)
Developer เลือก Callback หรือ Await ตาม Code Style และ Use Case
㉚ Await กับ Callback แบบไหนดีกว่า
ไม่มีคำตอบตายตัว
await เหมาะกับ Code ที่ต้องทำตามลำดับ
Query
↓
Validation
↓
Action
Callback เหมาะกับ Event-driven Flow บางแบบ
สิ่งสำคัญกว่าคือ Code ต้อง
อ่านง่าย
จัด Error ได้
ไม่ Query เกินจำเป็น
㉛ Parameterized Query คืออะไร
แทนที่จะประกอบ SQL String
ไม่ควรทำ
local sql =
"SELECT * FROM users WHERE name = '"
.. name
.. "'"
ควรใช้
MySQL.single.await(
[[
SELECT *
FROM users
WHERE name = ?
LIMIT 1
]],
{
name
}
)
ค่า name ถูกส่งแยกจาก SQL Structure
㉜ ทำไม Parameterized Query สำคัญ
ช่วยลดความเสี่ยง SQL Injection แบบทั่วไป และทำให้ Code อ่านง่ายขึ้น
Pattern คือ
SQL Structure
+
Parameters
แยกจากกัน
ไม่ใช่
SQL
+
Client Input
+
String Concatenation
㉝ Placeholder แบบ ? ใช้อย่างไร
ตัวอย่าง
SELECT *
FROM vehicles
WHERE character_id = ?
AND stored = ?
Parameters
{
characterId,
1
}
ค่าจะถูก Bind ตามลำดับ
? #1 → characterId
? #2 → 1
㉞ Named Placeholders มีไหม
oxmysql มีระบบ Parameter Handling ที่รองรับรูปแบบต่าง ๆ ตาม API/Driver
แต่ Resource ใหม่ควรใช้ Syntax ที่ตรงกับ Documentation และ Version ที่ใช้งานจริง
เพื่อหลีกเลี่ยงการ Copy Syntax เก่าจาก mysql-async โดยไม่เข้าใจ
㉟ oxmysql รองรับ mysql-async เดิมไหม
หนึ่งในเหตุผลสำคัญที่ oxmysql ถูกใช้งานกว้างคือมี Compatibility Layer สำหรับ Code ที่มาจาก mysql-async
Library MySQL.lua มี Aliases เช่นแนวคิด
MySQL.Async.fetchAll
→ MySQL.query
และ
MySQL.Sync.fetchAll
→ MySQL.query.await
ช่วยให้ Resource เก่า Migration ได้ง่ายขึ้น
㊱ ghmattimysql ล่ะ
oxmysql ถูกสร้างมาเป็นตัวแทนทั้ง
mysql-async
ghmattimysql
พร้อม Expanded API
จึงช่วยลดกรณี Server ต้องใช้ Database Wrappers หลายตัวพร้อมกัน
㊲ ควรติดตั้ง mysql-async กับ oxmysql พร้อมกันไหม
ถ้า Resources สามารถใช้ Compatibility ของ oxmysql ได้แล้ว โดยทั่วไปไม่ควรเปิด Database Middleware หลายตัวโดยไม่มีเหตุผล
ต้องตรวจ Resource Dependencies ก่อน Migration
อย่าลบ Database Resource เก่าใน Production ทันทีโดยไม่ทดสอบ Script ทั้งหมด
㊳ ทำไม Script FiveM จำนวนมากใช้ oxmysql
เหตุผลหลักคือ
① API ครบ
② Lua await ใช้ง่าย
③ รองรับ JavaScript
④ Parameterized Queries
⑤ Prepared Queries
⑥ Transactions
⑦ Slow Query Debugging
⑧ Compatibility กับระบบเก่า
⑨ Framework Ecosystem รองรับกว้าง
⑩ ใช้ MySQL/MariaDB ได้
จึงเหมาะทั้ง Resource เล็กและ Framework Server ขนาดใหญ่
㊴ ใช้กับ ESX ได้ไหม
ได้
ESX Resources จำนวนมากต้องใช้ Database สำหรับ
Users
Jobs
Vehicles
Inventory
Accounts
และสามารถใช้ oxmysql เป็น Database Layer ได้ตาม Version/Architecture ของ ESX Resource ที่ใช้
㊵ ใช้กับ QBCore ได้ไหม
ได้
QBCore Documentation และ Recipes จำนวนมากใช้ oxmysql เป็น Database Wrapper
Flow คือ
QBCore
↓
Server Resources
↓
oxmysql
↓
Database
แต่ต้องใช้ Version ที่ Framework/Resources รองรับ
㊶ ใช้กับ Qbox ได้ไหม
ได้เช่นกัน
Qbox/OX Ecosystem มีความสัมพันธ์กับ Libraries กลุ่ม OX อย่างมาก
แต่ oxmysql ไม่ได้ผูกกับ Framework ใด Framework หนึ่ง
Standalone Resource ก็สามารถใช้ได้
㊷ ใช้แบบ Standalone ได้ไหม
ได้
ตัวอย่าง Resource ง่าย ๆ
player_notes
ไม่ต้องใช้ ESX/QB/Qbox
เพียง
dependency 'oxmysql'
server_script '@oxmysql/lib/MySQL.lua'
server_script 'server.lua'
จากนั้น Query Database ได้โดยตรง
㊸ oxmysql รองรับ JavaScript ไหม
รองรับ
Package ปัจจุบันอธิบายตัวเองว่าเป็นการสื่อสารระหว่าง FXServer กับ MySQL ผ่าน node-mysql2
JavaScript Resources สามารถใช้ Exports หรือ Package/Type Support ตาม Documentation ได้
ดังนั้นไม่ได้จำกัดเฉพาะ Lua Developer
㊹ oxmysql ใช้ mysql2 หรือไม่
ใช่ Project ปัจจุบันระบุว่าเป็น
FXServer to MySQL communication
via node-mysql2
และ Package มี Dependency กับ mysql2
นี่คือ Database Driver Layer ภายในของ oxmysql
Developer Resource ทั่วไปไม่จำเป็นต้องเรียก mysql2 เอง
㊺ MySQL หรือ MariaDB เลือกอะไร
Documentation oxmysql แนะนำ MariaDB เพื่อ Compatibility กับ Resources FiveM จำนวนมากที่เดิมออกแบบจาก MySQL 5.7-era behavior
MySQL 8 ก็รองรับ แต่ Resource เก่าบางตัวอาจพบ Compatibility เช่น Reserved Keywords หรือ Schema Differences
ดังนั้นต้องดู Server Stack จริง
㊻ XAMPP ควรใช้ไหม
Documentation oxmysql ไม่แนะนำให้ติดตั้ง XAMPP เพียงเพื่อรัน Database
XAMPP เป็น Web Development Stack ที่รวมหลาย Service
สำหรับ Production FiveM Database ควรติดตั้ง Database Server โดยตรง เช่น MariaDB ตาม Architecture ที่เหมาะสม
㊼ Slow Query Warning คืออะไร
oxmysql สามารถเตือนเมื่อ Query ใช้เวลานานเกิน Threshold
ตัวอย่าง Config
set mysql_slow_query_warning 150
หมายถึง Threshold ตามหน่วยที่ Library กำหนดสำหรับ Warning
ช่วยหา Query ที่อาจต้องตรวจ Performance
㊽ Slow Query แปลว่า SQL แย่เสมอไหม
ไม่
Documentation ระบุว่า Query Time อาจได้รับผลจาก
Server Hitch
Startup Load
Database Load
Hardware
Network
ดังนั้น Warning คือสัญญาณให้ตรวจ
ไม่ใช่หลักฐานว่า SQL Query เขียนผิดเสมอ
㊾ mysql_debug คืออะไร
สามารถเปิด Debug Queries
set mysql_debug true
เพื่อดู Queries ใน Server Console/Debug Tools ตามระบบ oxmysql
มีประโยชน์ตอน Development และหา Query ที่ยิงบ่อยเกินไป
㊿ ควรเปิด mysql_debug ตลอด Production ไหม
ไม่จำเป็น
Debug Logging จำนวนมากสามารถสร้าง Log Volume และ Noise
เปิดเมื่อต้องตรวจปัญหา หรือจำกัดเฉพาะ Resources ที่ต้องการ Debug ตาม Configuration ที่ oxmysql รองรับ
Production ควรเปิดเท่าที่จำเป็น
51 Query มากเกินไปแก้อย่างไร
สมมติ Resource ทำ
Player 200 คน
×
10 Queries ต่อวินาที
=
2,000 Queries/วินาที
อาจไม่จำเป็นเลย
ควรพิจารณา
Runtime Cache
Batch Query
Events
Indexes
ลด Polling
ลด N+1 Queries
ก่อนเพิ่ม Hardware
52 อย่า Query Database ทุก Tick
ไม่ควร
CreateThread(function()
while true do
Wait(0)
MySQL.query.await(
'SELECT * FROM users'
)
end
end)
นี่เป็น Database Architecture ที่ผิดอย่างชัดเจน
Database เหมาะกับ Persistent Data ไม่ใช่ Frame-level Gameplay State
53 oxmysql กับ Cache ควรใช้ร่วมกันอย่างไร
ตัวอย่าง
Player Login
↓
oxmysql SELECT
↓
Runtime Player Data
↓
Gameplay
↓
Save changes
↓
oxmysql UPDATE
ช่วยลด Query จำนวนมาก
แต่ Cache ต้องมี Save/Recovery Strategy ที่เหมาะสม
54 oxmysql ป้องกัน Cheat ไหม
ไม่
oxmysql ทำหน้าที่ Database Communication
ถ้า Server Event เขียนแบบนี้
RegisterNetEvent(
'money:set',
function(amount)
MySQL.update.await(
[[
UPDATE characters
SET cash = ?
WHERE id = ?
]],
{
amount,
getCharacterId(source)
}
)
end
)
Client ยังสามารถพยายามส่ง amount ที่โกงได้
ปัญหาอยู่ที่ Server Validation
ไม่ใช่ oxmysql
55 Pattern ที่ถูกต้องสำหรับ Economy
ควรเป็น
Client
↓
ขอทำ Action
Server
↓
ตรวจ Player
↓
ตรวจ State
↓
ตรวจ Permission
↓
คำนวณ Amount เอง
↓
oxmysql
↓
Persist
Client ไม่ควรเป็น Source of Truth ของเงิน
56 oxmysql ทำให้ SQL Injection หายหมดไหม
ไม่ควรพูดว่าหาย 100%
Parameterized Queries ช่วยป้องกัน SQL Injection ในรูปแบบทั่วไปได้มาก
แต่ Developer ยังสามารถเขียน Dynamic SQL ที่อันตรายเองได้ เช่นนำ
table name
column name
ORDER BY
raw SQL fragments
จาก Untrusted Client Input ไปประกอบ Query
ต้อง Validate Architecture ทั้งระบบ
57 อย่าให้ Client ส่งชื่อ Table
ตัวอย่างอันตราย
RegisterNetEvent(
'db:read',
function(tableName)
local query =
'SELECT * FROM '
.. tableName
MySQL.query.await(
query
)
end
)
ไม่ควรเปิด Generic Database API ให้ Client
Server Event ควรเป็น Business Action เช่น
garage:getVehicles
ไม่ใช่
database:runQuery
58 oxmysql Resource Restart ต้องระวังไหม
มาก
ถ้า Resources จำนวนมากพึ่ง oxmysql แล้วคุณ
restart oxmysql
กลาง Production
Queries/Consumers อาจได้รับผลกระทบ
บาง Resource อาจ Initialize Database API ตอน Start เพียงครั้งเดียว
การ Restart Critical Database Dependency จึงควรทำใน Maintenance Context ที่เหมาะสม
59 Checklist ก่อนใช้ oxmysql
ตรวจ
① Database Server ติดตั้งหรือยัง?
② MySQL หรือ MariaDB?
③ Database ถูกสร้างหรือยัง?
④ Database User มีหรือยัง?
⑤ Password ปลอดภัยหรือไม่?
⑥ Connection String ถูกหรือไม่?
⑦ mysql_connection_string อยู่ก่อน oxmysql หรือไม่?
⑧ oxmysql Start ก่อน Consumer หรือไม่?
⑨ Resource ประกาศ dependency หรือไม่?
⑩ MySQL.lua Import ก่อน Server Scripts หรือไม่?
⑪ ใช้ query กับหลาย Rows ถูกไหม?
⑫ ใช้ single กับหนึ่ง Row ถูกไหม?
⑬ ใช้ scalar กับหนึ่ง Value ถูกไหม?
⑭ ใช้ insert เมื่ออยากได้ Insert ID หรือไม่?
⑮ ใช้ update เมื่ออยากได้ affected rows หรือไม่?
⑯ Transaction ใช้เมื่อ Operation ต้อง Atomic หรือไม่?
⑰ Query ใช้ Parameters หรือไม่?
⑱ มี SQL String Concatenation จาก Client หรือไม่?
⑲ Client Input Validate หรือยัง?
⑳ Query อยู่ใน Loop หนักหรือไม่?
㉑ มี N+1 Query หรือไม่?
㉒ Index เหมาะหรือไม่?
㉓ Slow Query Warning ถูกตรวจหรือไม่?
㉔ Debug เปิดโดยมีเหตุผลหรือไม่?
㉕ Backup Database มีหรือไม่?
⑥⓪ ตัวอย่าง Resource ใช้ oxmysql แบบพื้นฐาน
โครงสร้าง
com_players/
├── fxmanifest.lua
└── server/
└── main.lua
fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
author 'comsiam'
description 'FiveM oxmysql example'
version '1.0.0'
dependency 'oxmysql'
server_script '@oxmysql/lib/MySQL.lua'
server_script 'server/main.lua'
server.cfg
set mysql_connection_string "mysql://username:password@localhost:3306/fivem"
ensure oxmysql
ensure com_players
server/main.lua
local function getCharacter(
characterId
)
if type(characterId)
~= 'number' then
return nil
end
local character =
MySQL.single.await(
[[
SELECT
id,
firstname,
lastname,
cash,
bank
FROM characters
WHERE id = ?
LIMIT 1
]],
{
characterId
}
)
return character
end
ตัวอย่าง Command
RegisterCommand(
'dbtest',
function(source)
if source <= 0 then
return
end
local characterId = 1
local character =
getCharacter(
characterId
)
if not character then
print(
'Character not found'
)
return
end
print(
character.firstname,
character.lastname,
character.cash
)
end,
false
)
Flow คือ
/dbtest
↓
Server Resource
↓
MySQL.single.await
↓
oxmysql
↓
MariaDB/MySQL
↓
SELECT Row
↓
Lua Table
↓
Server Logic
นี่คือพื้นฐานที่ Resource Database FiveM จำนวนมากใช้
ตัวอย่างใช้ query
local vehicles =
MySQL.query.await(
[[
SELECT
id,
plate,
model,
stored
FROM vehicles
WHERE character_id = ?
]],
{
characterId
}
)
แล้ว
for i = 1, #vehicles do
local vehicle =
vehicles[i]
print(
vehicle.id,
vehicle.plate
)
end
เหมาะกับหลาย Rows
ตัวอย่างใช้ scalar
local vehicleCount =
MySQL.scalar.await(
[[
SELECT COUNT(*)
FROM vehicles
WHERE character_id = ?
]],
{
characterId
}
)
print(
'Vehicles:',
vehicleCount
)
เหมาะกับค่าหนึ่งค่า
ตัวอย่างใช้ insert
local id =
MySQL.insert.await(
[[
INSERT INTO vehicles (
character_id,
plate,
model
)
VALUES (?, ?, ?)
]],
{
characterId,
plate,
model
}
)
if not id then
return
end
print(
'Vehicle DB ID:',
id
)
ตัวอย่างใช้ update
local changed =
MySQL.update.await(
[[
UPDATE vehicles
SET stored = ?
WHERE id = ?
AND character_id = ?
]],
{
1,
vehicleId,
characterId
}
)
if changed < 1 then
return
end
print(
'Vehicle stored'
)
Notice ว่า Query ตรวจ
id
+
character_id
ไม่ใช่ Update ด้วย Vehicle ID ที่ Client ส่งมาอย่างเดียว
ทำไม oxmysql ถึงสำคัญกับ FiveM Developer
เพราะเมื่อ Script โตเกิน
Command
Marker
Local Effect
เกือบทุก Roleplay System จะเริ่มต้องการ Persistence
เช่น
สร้าง Character
↓
Database
ซื้อรถ
↓
Database
เก็บรถ
↓
Database
ซื้อบ้าน
↓
Database
เปลี่ยน Job
↓
Database
ดังนั้น Developer ที่เขียน Resource จริงควรเข้าใจทั้ง
FiveM Server Logic
+
SQL
+
oxmysql API
+
Database Design
ไม่ควรเรียนแค่การ Copy Query จาก Script อื่น
คำถามที่พบบ่อยเกี่ยวกับ oxmysql FiveM
oxmysql FiveM คืออะไร
คือ Resource/Library ที่เชื่อม FXServer กับ MySQL หรือ MariaDB
oxmysql เป็น Database ไหม
ไม่ เป็น Database Connector
ต้องติดตั้ง MySQL หรือ MariaDB ด้วยไหม
ต้องมี Database Server ที่ oxmysql สามารถเชื่อมต่อได้
oxmysql ใช้กับ MariaDB ได้ไหม
ได้ และ Documentation แนะนำ MariaDB เพื่อ Compatibility กับ FiveM Resources จำนวนมาก
ใช้กับ MySQL 8 ได้ไหม
ได้ โดย oxmysql ถูกออกแบบพร้อม Compatibility ที่ดีขึ้นสำหรับ MySQL 8 แต่ Resource เก่าบางตัวอาจมี Schema/SQL Compatibility Issues
oxmysql ใช้กับ ESX ได้ไหม
ได้หาก Resource/Version นั้นรองรับ
ใช้กับ QBCore ได้ไหม
ได้ และพบใน QBCore Stack จำนวนมาก
ใช้กับ Qbox ได้ไหม
ได้
ใช้ Standalone ได้ไหม
ได้
oxmysql ต้อง Start ก่อน Core ไหม
ถ้า Core ต้องใช้ Database ควรให้ oxmysql พร้อมก่อน
Manifest เขียนอย่างไร
dependency 'oxmysql'
server_script '@oxmysql/lib/MySQL.lua'
Connection String อยู่ตรงไหน
โดยทั่วไปตั้งใน server.cfg ก่อน Start oxmysql
MySQL.query ใช้ทำอะไร
ใช้เมื่อ Query คืนหลาย Rows/Result Set
MySQL.single ใช้ทำอะไร
คืน Row เดียว
MySQL.scalar ใช้ทำอะไร
คืนค่า Column แรกจาก Row เดียว
MySQL.insert ใช้ทำอะไร
เหมาะกับ Insert และคืน Insert ID
MySQL.update ใช้ทำอะไร
คืนจำนวน Rows ที่ได้รับผลจาก Operation
MySQL.prepare ใช้ทำอะไร
Prepared Query API สำหรับ Query ที่เหมาะสม
MySQL.transaction ใช้ทำอะไร
ทำหลาย Database Operations ใน Transaction เดียว
.await คืออะไร
รอ Result ใน Lua Coroutine แล้วจึงทำ Code ต่อ
Callback ยังใช้ได้ไหม
ได้
ใช้ await หรือ callback ดี
เลือกตาม Architecture; await มักทำให้ Sequential Database Logic อ่านง่าย
Parameter Placeholder ใช้อะไร
เช่น
WHERE id = ?
แล้วส่ง Parameters แยก
oxmysql ป้องกัน SQL Injection ไหม
Parameterized Queries ช่วยลดความเสี่ยงรูปแบบทั่วไป แต่ Developer ยังต้องไม่สร้าง Dynamic SQL จาก Untrusted Input โดยไม่ตรวจ
oxmysql ป้องกัน Cheat ไหม
ไม่ Server Validation ยังจำเป็น
Query Database ทุก Tick ได้ไหม
ไม่ควร
Slow Query Warning คืออะไร
ระบบเตือน Queries ที่ใช้เวลานานเกิน Threshold
Slow Query แปลว่า Database พังไหม
ไม่เสมอ Server Hitch และ Startup Load ก็มีผล
mysql_debug ใช้ทำอะไร
ช่วย Debug Queries ที่ Resources ส่งผ่าน oxmysql
oxmysql ใช้แทน mysql-async ได้ไหม
มี Compatibility Layer และถูกออกแบบมาเป็น Replacement สำหรับ mysql-async/ghmattimysql แต่ Resource จริงควรถูกทดสอบก่อน Migration
ควรรัน mysql-async และ oxmysql พร้อมกันไหม
ไม่ควรเพิ่ม Database Wrappers หลายตัวโดยไม่มีเหตุผล หาก Resources สามารถรวมมาใช้ oxmysql ได้ควรออกแบบ Stack ให้ชัดเจน
สรุป oxmysql FiveM คืออะไร ทำไม Script จำนวนมากถึงใช้
oxmysql คือ Database Connector สำหรับ FXServer ที่ทำให้ FiveM Server Scripts ติดต่อ MySQL/MariaDB ได้ผ่าน API ที่ออกแบบมาใช้งานสะดวกกับ Resource Architecture
มันอยู่ตรงกลางระหว่าง
FiveM Server Scripts
↓
oxmysql
↓
MySQL / MariaDB
และให้ APIs สำคัญอย่าง
query
single
scalar
insert
update
prepare
transaction
ทำให้ Developer สามารถเลือก Method ให้เหมาะกับ Result ที่ต้องการ
หลาย Rows
→ query
หนึ่ง Row
→ single
หนึ่ง Value
→ scalar
สร้าง Row + ต้องการ ID
→ insert
แก้ข้อมูล + ต้องการ affected rows
→ update
หลาย Operations ต้อง Atomic
→ transaction
อีกเหตุผลที่ oxmysql ถูกใช้อย่างกว้างใน FiveM Ecosystem คือมี Compatibility กับ APIs รุ่นเก่าอย่าง mysql-async และ ghmattimysql ทำให้ Resource จำนวนมากสามารถ Migration หรือใช้งานร่วมกับ Stack ปัจจุบันได้ง่ายขึ้น
สำหรับ Resource Lua โครงสร้างที่พบได้บ่อยคือ
dependency 'oxmysql'
server_script '@oxmysql/lib/MySQL.lua'
แล้วใน Server Code
local player =
MySQL.single.await(
[[
SELECT id, cash
FROM characters
WHERE id = ?
LIMIT 1
]],
{
characterId
}
)
แต่สิ่งสำคัญที่สุดไม่ใช่เพียงจำ Method ของ oxmysql
Developer ต้องเข้าใจด้วยว่า
Client
≠ Database Authority
Client ควรส่งเพียง Request
Server เป็นผู้
Validate
Calculate
Authorize
Query
Update
ข้อมูล Database
สำหรับผู้ที่เรียน FiveM Developer กับ comsiam การใช้ oxmysql ที่ดีจึงไม่ได้หมายถึง Query ได้เยอะที่สุด แต่คือ Query เท่าที่จำเป็น ใช้ Parameters อย่างถูกต้อง ออกแบบ Index และ Transaction ให้เหมาะ และแยก Runtime State ออกจาก Persistent State อย่างชัดเจน
หลักสำคัญจาก comsiam คือ อย่ามอง oxmysql เป็นที่เก็บข้อมูล แต่ให้มองมันเป็นสะพาน — ถ้า Database Schema, SQL หรือ Server Validation ออกแบบไม่ดี ต่อให้ Connector เร็วเพียงใด ระบบก็ยังช้า ผิดพลาด หรือไม่ปลอดภัยได้
หัวข้อถัดไปคือ วิธีเชื่อม FiveM กับ MySQL หรือ MariaDB ด้วย oxmysql ซึ่งจะทำแบบ Step-by-step ตั้งแต่สร้าง Database/User, ตั้ง mysql_connection_string, ติดตั้ง oxmysql, จัด server.cfg, เขียน fxmanifest.lua และทดสอบ SELECT 1 จนรู้แน่นอนว่า FXServer เชื่อม Database สำเร็จ
Comments
Post a Comment