FiveM Database คืออะไร? วิธีใช้ MySQL, MariaDB และ oxmysql กับ FiveM Server อย่างถูกต้อง
FiveM Database คือฐานข้อมูลที่ Resource ใช้เก็บข้อมูลซึ่งต้องคงอยู่แม้ผู้เล่นออกจาก Server หรือ FXServer Restart เช่น Character Profile, Vehicle Records, Settings, Permissions, Statistics และข้อมูลระบบต่างๆ
สิ่งสำคัญคือ FXServer ไม่ได้บังคับว่าทุก FiveM Server ต้องมี MySQL ตัวอย่างเช่น txAdmin มี Player Database ของตัวเองและระบุว่าไม่จำเป็นต้องใช้ MySQL แต่ Resources และ Framework จำนวนมากเลือกใช้ MySQL/MariaDB เพื่อเก็บข้อมูลถาวรของ Gameplay.
หนึ่งใน Database Resources ที่ใช้กับ FXServer คือ oxmysql ซึ่งโครงการระบุว่าเป็น Resource สำหรับติดต่อ MySQL Database และทำงานผ่าน node-mysql2 พร้อม API สำหรับ Query, Insert, Update, Single, Scalar, Prepare และ Transaction.
① FiveM Database มีไว้ทำอะไร
ลองแยกข้อมูล FiveM ออกเป็น 2 ประเภท
Runtime Data
=
ข้อมูลที่อยู่เฉพาะตอน Server เปิด
Persistent Data
=
ข้อมูลที่ต้องอยู่หลัง Restart
ตัวอย่าง Runtime Data:
Entity Handle
Network ID
Current Entity Owner
Temporary Session
Routing Bucket
ตัวอย่าง Persistent Data:
Character ID
ชื่อ Character
Vehicle Record
User Settings
Statistics
Account-related Data
Persistent Data คือกลุ่มที่เหมาะกับ Database
② Database ไม่เหมือน State Bag
State Bag เป็น Runtime State ของ FiveM
ส่วน Database เป็น Persistent Storage
จำง่ายๆ:
State Bag
= ข้อมูลระหว่าง Runtime
Database
= ข้อมูลที่ต้องการเก็บถาวร
ดังนั้นไม่ควรใช้ State Bag เป็น Database และไม่ควรใช้ Database Query แทน State Bag ทุกครั้งที่ Runtime State เปลี่ยน
③ Database ไม่เหมือน Lua Table
ตัวอย่าง:
local players = {}
Table นี้อยู่ใน Memory ของ Resource
เมื่อ Resource Restart:
players
→ ถูกสร้างใหม่
แต่ข้อมูลใน Database ยังคงอยู่จนกว่าจะถูกแก้ไขหรือลบ
จึงเหมาะกับข้อมูลที่ต้อง Restore หลัง Restart
④ FiveM ต้องใช้ MySQL หรือ MariaDB ไหม
ตัว FXServer เองไม่ได้กำหนดว่าต้องใช้ MySQL สำหรับทุก Server แต่ Database-backed Resources จำนวนมากต้องการ Database Wrapper หรือ Database Server ตามที่ Resource นั้นออกแบบไว้
oxmysql รองรับการเชื่อมต่อ MySQL-compatible Database และเอกสารของโครงการแนะนำ MariaDB สำหรับ Compatibility กับ FiveM Resources โดยเฉพาะ Resources รุ่นเก่าที่ออกแบบมาสำหรับ MySQL 5.7.
⑤ MySQL กับ MariaDB ต่างกันอย่างไรสำหรับ FiveM
ทั้งสองเป็นระบบฐานข้อมูลเชิงสัมพันธ์ที่ Resources สามารถใช้งานได้ตาม Compatibility ของ Database Wrapper
เอกสาร oxmysql ระบุว่า Resources FiveM จำนวนมากเดิมออกแบบกับ MySQL 5.7 และอาจพบ Compatibility Issues กับ MySQL 8 เช่น Reserved Keywords เพิ่มขึ้นหรือพฤติกรรมบางอย่างของ LONGTEXT/JSON จึงแนะนำ MariaDB เป็นตัวเลือกที่เข้ากันได้ดี.
สำหรับ Server จริงควรดู Requirement ของ Framework/Resources ที่ใช้อยู่ด้วย ไม่ควรเปลี่ยน Database Engine กลาง Production โดยไม่ทดสอบ
⑥ oxmysql คืออะไร
oxmysql เป็น FiveM Resource สำหรับสื่อสารกับ MySQL Database
โครงการอธิบายตัวเองว่า:
FiveM Resource
↓
oxmysql
↓
node-mysql2
↓
MySQL / MariaDB
โดยมี API สำหรับ Lua และ JavaScript.
⑦ oxmysql จำเป็นสำหรับ FiveM ทุก Server ไหม
ไม่
Resource ต้องใช้หรือไม่ขึ้นอยู่กับ Architecture
ตัวอย่าง:
Server A
ไม่มี Gameplay Database
→ อาจไม่ต้องใช้
Server B
Framework ต้องใช้ oxmysql
→ ต้องติดตั้ง
Server C
ใช้ Database Wrapper ตัวอื่น
→ ไม่จำเป็นต้องมี oxmysql
อย่าติดตั้ง Database Resource เพียงเพราะเห็น Server อื่นใช้ ให้ดู Dependencies ของ Resources ก่อน
⑧ วิธีติดตั้ง oxmysql โดยหลักการ
เอกสาร oxmysql ระบุขั้นตอนหลักคือ:
ติดตั้ง Database Server
ดาวน์โหลด Release ของ oxmysql
ให้
oxmysqlStart ก่อน Resources ที่ต้องเรียก Databaseตั้ง
mysql_connection_stringก่อนเริ่ม Resources เหล่านั้น
โครงสร้าง server.cfg เช่น:
set mysql_connection_string "mysql://USER:PASSWORD@127.0.0.1:3306/DATABASE"
ensure oxmysql
ensure my_resource
อย่าใช้ Username, Password หรือ Database Name จากตัวอย่างกับ Production จริง
⑨ ทำไม oxmysql ต้อง Start ก่อน Resource ที่ใช้ Database
ถ้า Resource เรียก Export ของ oxmysql ก่อน oxmysql พร้อม อาจพบ Error ประเภท:
No such export in resource oxmysql
Official oxmysql Issue Documentation ระบุหนึ่งในสาเหตุคือ oxmysql ยังไม่ Start ตอน Resource อื่นเรียก Export และแนะนำให้ oxmysql อยู่ต้นๆ ของ Resource Start Order.
ดังนั้น:
oxmysql
↓
framework
↓
resources ที่ใช้ database
เป็นลำดับที่ควรตรวจ
⑩ mysql_connection_string คืออะไร
คือ Connection String ที่ใช้บอก Database Wrapper ว่าจะเชื่อมไปยัง Database ไหน
ข้อมูลหลักประกอบด้วย:
Host
Port
Username
Password
Database Name
oxmysql Documentation รองรับ Connection String มากกว่าหนึ่ง Format และกำหนดผ่าน set mysql_connection_string.
⑪ ตัวอย่าง Connection String แบบ URL
set mysql_connection_string "mysql://fivem_user:CHANGE_ME@127.0.0.1:3306/fivem"
โครงสร้าง:
mysql://
username
:
password
@
host
:
port
/
database
ใช้ค่าจริงของ Server เท่านั้นและเก็บ Credential อย่างระมัดระวัง
⑫ ตัวอย่าง Connection String อีก Format
oxmysql รองรับ Format เช่น:
set mysql_connection_string "user=fivem_user;password=CHANGE_ME;host=127.0.0.1;port=3306;database=fivem"
Documentation ยังเตือนว่า Special Characters บางตัวอาจสร้างปัญหาใน Connection String และแนะนำให้ลองเปลี่ยน Format หากพบ Connection Problem.
⑬ Database ต่อไม่ได้ควรตรวจอะไร
เริ่มจาก:
① Database Server Running?
② Database มีจริง?
③ Username ถูก?
④ Password ถูก?
⑤ Host ถูก?
⑥ Port ถูก?
⑦ Connection String ถูก?
⑧ oxmysql Start แล้ว?
⑨ Resource Start Order ถูก?
Official oxmysql Common Issues ระบุว่าปัญหา Unable to establish a connection มักต้องตรวจ mysql_connection_string, ตรวจว่า Database มีอยู่และกำลังทำงาน และตรวจ Format/Special Characters ของ Connection String.
⑭ fxmanifest.lua เกี่ยวข้องกับ Database อย่างไร
FiveM Resource ใช้ fxmanifest.lua กำหนด Scripts และ Metadata ของ Resource โดย Cfx.re ระบุว่า server_script ใช้กำหนด Script ที่โหลดฝั่ง Server.
ตัวอย่าง Resource:
fx_version 'cerulean'
game 'gta5'
server_script 'server.lua'
Database Logic ที่ควรเป็น Server-side จึงสามารถอยู่ใน server.lua
⑮ Database Query ควรทำ Client-side ไหม
โดยทั่วไป ไม่ควรให้ Client เชื่อม Database โดยตรง
Architecture ที่เหมาะกว่า:
Client
↓
ขอข้อมูลที่จำเป็น
Server
↓
Validate Request
↓
Database Query
↓
Server Process Result
↓
ส่งข้อมูลที่อนุญาตกลับ Client
ช่วยให้ Database Credentials และ Query Authority อยู่ฝั่ง Server
⑯ อย่าใส่ Database Password ใน client.lua
client_script ถูกโหลดให้ Client ตามระบบ Resource ของ FiveM ขณะที่ server_script ทำงานฝั่ง Server.
ดังนั้นข้อมูลสำคัญ เช่น:
Database Password
Database Username
Private Connection Information
ไม่ควรถูกใส่ใน Client Code ที่ผู้เล่นได้รับ
⑰ SQL Table คืออะไร
Database หนึ่งสามารถมีหลาย Tables
ตัวอย่าง:
Database: fivem
users
characters
vehicles
settings
statistics
แต่ละ Table มี Columns เช่น:
characters
├── id
├── identifier
├── firstname
├── lastname
└── created_at
การออกแบบ Schema ที่ดีช่วยให้ Query ง่ายและลดข้อมูลซ้ำ
⑱ Primary Key คืออะไร
Primary Key ใช้ระบุ Row ที่ไม่ซ้ำกัน
ตัวอย่าง:
CREATE TABLE characters (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
identifier VARCHAR(64) NOT NULL,
firstname VARCHAR(50) NOT NULL,
lastname VARCHAR(50) NOT NULL,
PRIMARY KEY (id)
);
ในระบบ Character:
id = 152
ควรหมายถึง Record นั้นอย่างชัดเจน
⑲ อย่าใช้ FiveM NetID เป็น Database Primary Key
Network ID เป็น Runtime Network Reference ของ Entity
เมื่อ Entity หาย NetID สามารถถูก Reuse ในภายหลังได้
ดังนั้น Persistent Vehicle ควรใช้:
vehicle_id
ของ Database เอง
ไม่ใช่:
Network ID
เป็นตัวตนถาวร
⑳ Player Server ID ใช้เป็น Database ID ได้ไหม
ไม่ควร
Server ID เป็น Runtime Connection Identifier
ผู้เล่น Disconnect แล้วกลับมาใหม่อาจไม่ได้ Server ID เดิม
Persistent Record ควรอิง Identifier/Application ID ที่ระบบออกแบบไว้ ไม่ใช่ Source ID ปัจจุบันเพียงอย่างเดียว
㉑ SELECT คืออะไร
ใช้ดึงข้อมูลจาก Database
ตัวอย่าง:
SELECT firstname, lastname
FROM characters
WHERE identifier = ?
oxmysql มี MySQL.query ซึ่งเมื่อใช้ SELECT จะคืน Matching Rows และ Columns.
㉒ MySQL.query ใช้อย่างไร
ตัวอย่าง Lua:
local rows = MySQL.query.await(
'SELECT `id`, `firstname`, `lastname` FROM `characters` WHERE `identifier` = ?',
{
identifier
}
)
if rows then
for i = 1, #rows do
local row = rows[i]
print(
row.id,
row.firstname,
row.lastname
)
end
end
รูปแบบ MySQL.query.await() และ ? Parameter Placeholder ถูกระบุใน oxmysql Documentation.
㉓ .await คืออะไร
ใน Lua API ของ oxmysql มี Promise/Await Style เช่น:
local rows =
MySQL.query.await(...)
ทำให้ Function รอผล Query ก่อนทำ Logic บรรทัดถัดไปใน Coroutine Context ที่รองรับ
อย่าสับสน .await กับการทำ Database Query แบบ Blocking ทั้ง FXServer Process
ควรวัด Query Performance จริงเมื่อสงสัยว่าช้า
㉔ Callback ใช้ได้ไหม
ได้
oxmysql รองรับ Callback Style เช่น:
MySQL.query(
'SELECT `id` FROM `characters` WHERE `identifier` = ?',
{
identifier
},
function(rows)
if not rows then
return
end
print(#rows)
end
)
Documentation รองรับทั้ง Promise/Await และ Callback API.
㉕ MySQL.single คืออะไร
หากคาดหวังเพียง หนึ่ง Row สามารถใช้:
local row =
MySQL.single.await(
'SELECT `id`, `firstname`, `lastname` FROM `characters` WHERE `id` = ? LIMIT 1',
{
characterId
}
)
MySQL.single คืน Columns ของ Row เดียวแทน Array หลาย Rows.
㉖ query กับ single เลือกอะไร
จำง่ายๆ:
query
= ต้องการหลาย Rows
single
= ต้องการหนึ่ง Row
ตัวอย่าง:
รายชื่อ Character ทั้งหมด
→ query
Character ID 25
→ single
เลือก Function ให้ตรงกับ Result ที่ต้องการ จะทำให้ Code อ่านง่ายขึ้น
㉗ MySQL.scalar คืออะไร
Scalar เหมาะเมื่อ Query ต้องการค่าเดียว
ตัวอย่างเชิงแนวคิด:
SELECT COUNT(*)
FROM characters
WHERE identifier = ?
หรือดึง Column เดียวจาก Row เดียว
ช่วยไม่ต้องรับ Table ขนาดใหญ่เมื่อ Resource ต้องการเพียงค่าเดียว
㉘ INSERT คืออะไร
ใช้เพิ่ม Row ใหม่
ตัวอย่าง:
INSERT INTO characters
(identifier, firstname, lastname)
VALUES (?, ?, ?)
oxmysql มี MySQL.insert ซึ่งคืน Insert ID เมื่อ Row มี ID ที่ถูกสร้างขึ้น.
㉙ ตัวอย่าง MySQL.insert
local characterId =
MySQL.insert.await(
[[
INSERT INTO `characters`
(`identifier`, `firstname`, `lastname`)
VALUES (?, ?, ?)
]],
{
identifier,
firstName,
lastName
}
)
if characterId then
print(
('Created character id=%s')
:format(characterId)
)
end
MySQL.insert.await() ใช้ Return Insert ID ตาม API ของ oxmysql.
㉚ UPDATE คืออะไร
ใช้แก้ Row ที่มีอยู่
ตัวอย่าง:
UPDATE characters
SET firstname = ?
WHERE id = ?
oxmysql มี MySQL.update และคืนจำนวน Rows ที่ได้รับผลจาก Query.
㉛ ตัวอย่าง MySQL.update
local affectedRows =
MySQL.update.await(
'UPDATE `characters` SET `firstname` = ? WHERE `id` = ?',
{
newName,
characterId
}
)
print(
('Updated rows: %s')
:format(affectedRows)
)
ถ้า:
affectedRows = 0
ควรตรวจว่า Record มีจริงหรือค่าไม่ได้เปลี่ยนตาม Behavior ของ Database/Query
㉜ DELETE คืออะไร
ใช้ลบ Record
ตัวอย่าง:
DELETE FROM characters
WHERE id = ?
การ Delete ข้อมูลถาวรควรมี Server-side Validation ที่ชัดเจนและมักควรอ้าง Internal ID ที่ Server ตรวจได้
อย่าให้ Client ส่ง ID มาแล้ว Server Delete ทันทีโดยไม่ตรวจเจ้าของ Record
㉝ Placeholder คืออะไร
แทนการต่อ String แบบนี้:
local query =
"SELECT * FROM characters WHERE identifier = '" ..
identifier ..
"'"
ให้ใช้:
MySQL.query.await(
'SELECT * FROM `characters` WHERE `identifier` = ?',
{
identifier
}
)
oxmysql ระบุว่า Placeholders ช่วยให้ Parameters ถูก Execute อย่างปลอดภัยและป้องกันวิธี SQL Injection ทั่วไปได้.
㉞ ทำไม String Concatenation ใน SQL ไม่ดี
ตัวอย่างที่ควรหลีกเลี่ยง:
local query =
"SELECT * FROM users WHERE name = '" ..
playerName ..
"'"
เพราะข้อมูลที่มาจาก User Input ถูกนำไปผสมกับ SQL Structure โดยตรง
ใช้ Parameterized Query แทน:
MySQL.single.await(
'SELECT * FROM `users` WHERE `name` = ? LIMIT 1',
{
playerName
}
)
Placeholders ถูกออกแบบเพื่อแยก Query Structure ออกจาก Parameter Values.
㉟ Named Placeholders ควรใช้ไหม
เอกสาร oxmysql ปัจจุบันระบุ Named Placeholders เช่น:
@identifier
ว่า deprecated และยกตัวอย่าง ? Placeholder เป็นรูปแบบหลัก.
ดังนั้น Resource ใหม่ควรพิจารณา:
WHERE identifier = ?
มากกว่า Code Style รุ่นเก่า
㊱ Prepared Statement คืออะไร
oxmysql มี:
MySQL.prepare.await(...)
Documentation ระบุว่า Prepare เหมาะกับ Frequently-called Queries และสามารถทำให้ Query ที่เรียกบ่อยทำงานได้เร็วขึ้น โดย Prepared Statement ถูกจัดการจาก MySQL Server.
㊲ ตัวอย่าง MySQL.prepare
local row =
MySQL.prepare.await(
'SELECT `firstname`, `lastname` FROM `characters` WHERE `id` = ?',
{
characterId
}
)
สำหรับ Prepare API เอกสารระบุให้ใช้ ? Value Placeholder.
㊳ ต้อง Prepare ทุก Query ไหม
ไม่จำเป็น
Prepare เหมาะกับ Query ที่:
รูปแบบเดิม
+
เรียกซ้ำบ่อย
+
เปลี่ยนเฉพาะ Parameters
Query ที่ถูกเรียกน้อยไม่ได้แปลว่าต้องเปลี่ยนเป็น Prepare ทุกตัว
ควร Profile/Benchmark ก่อน Optimize แบบเหมารวม
㊴ Transaction คืออะไร
Transaction ใช้เมื่อหลาย Queries ต้องสำเร็จ ทั้งหมด หรือไม่ก็ไม่ควรถูก Commit เลย
oxmysql ระบุว่า Transaction จะ Commit ก็ต่อเมื่อ Queries ทั้งหมดสำเร็จ หาก Query หนึ่งล้มเหลวจะไม่มี Query ใดใน Transaction ถูก Commit.
จำ:
Query A สำเร็จ
Query B สำเร็จ
Query C สำเร็จ
→ COMMIT
แต่ถ้า B ล้มเหลว
→ ไม่ Commit ทั้งชุด
㊵ Transaction เหมาะกับอะไร
ตัวอย่างระบบทั่วไป:
สร้าง Character Record
+
สร้าง Settings Record
+
สร้าง Profile Record
ถ้าระบบต้องการให้ทั้ง 3 Record มีอยู่พร้อมกัน Transaction ช่วยป้องกันสถานะที่ทำสำเร็จเพียงบางส่วน
㊶ ตัวอย่าง Transaction
local success =
MySQL.transaction.await({
{
query = [[
INSERT INTO `characters`
(`identifier`, `firstname`, `lastname`)
VALUES (?, ?, ?)
]],
values = {
identifier,
firstName,
lastName
}
},
{
query = [[
INSERT INTO `character_settings`
(`character_id`, `theme`)
VALUES (?, ?)
]],
values = {
characterId,
'default'
}
}
})
if not success then
print('Transaction failed')
end
แนวคิด Transaction และรูปแบบ Array ของ Query/Parameters รองรับโดย oxmysql.
ใน Production ต้องออกแบบค่าที่ Queries พึ่งพากันให้ถูกต้องตาม Schema และ Flow จริง
㊷ Transaction ไม่ได้ทำให้ Query เร็วขึ้นเสมอไป
หน้าที่หลักคือ Data Consistency
อย่าใช้ Transaction เพราะคิดว่า:
Transaction
=
Fast Mode
ให้ใช้เมื่อ Operations หลายรายการต้องสำเร็จเป็นชุดเดียวกัน
㊸ Database Query ช้าทำให้ FiveM Lag ได้ไหม
ได้ในแง่ที่ Resource ที่รอหรือทำงานหนักเกี่ยวกับ Database สามารถส่งผลต่อ Performance ได้
oxmysql มี Slow Query Warning และระบุว่าสามารถกำหนด Threshold ผ่าน:
set mysql_slow_query_warning 150
แต่เอกสารเตือนว่า Slow Query Warning ไม่ได้แปลว่า Database มีปัญหาเสมอ เพราะ Server Hitch ก็อาจทำให้เวลาที่วัดดูช้า และ Query ตอน Startup บางตัวอาจช้าโดยไม่ใช่ปัญหาร้ายแรง.
㊹ mysql_slow_query_warning คืออะไร
ใช้กำหนดเวลาที่ Query จะถูก Report ว่าช้า
ตัวอย่างจาก Documentation:
set mysql_slow_query_warning 150
ค่าอยู่ในหน่วย milliseconds ตามบริบทของ oxmysql Configuration.
อย่าลด Threshold ต่ำมากโดยไม่มีเหตุผล เพราะ Console อาจเต็มไปด้วย Warnings ที่ไม่มีประโยชน์
㊺ Slow Query Warning แปลว่า SQL พังไหม
ไม่
ให้ตรวจ:
Query เดิมช้าตลอด?
↓
Database CPU?
↓
Index?
↓
จำนวน Rows?
↓
Server Hitch?
↓
Startup Query?
oxmysql ระบุเองว่า Query Time อาจไม่แม่นสมบูรณ์ และ Slow Warning อาจมีสาเหตุจาก Server Hitch.
㊻ mysql_debug ช่วยอะไร
oxmysql Benchmark Documentation ระบุว่า Real Query Speeds สามารถดูได้จาก Debug UI และ Server Console เมื่อเปิด mysql_debug.
จึงมีประโยชน์เมื่อ:
Profiler ชี้ว่า Database Resource ช้า
↓
เปิด Database Debug
↓
ดู Query Time จริง
↓
หา Query ที่ควร Optimize
㊼ Database Performance ขึ้นกับอะไร
oxmysql ระบุว่า Query Speed แตกต่างกันมากตาม:
Hardware
Database Settings
Database Version
Current Workload
ดังนั้น Benchmark จาก Server คนอื่นไม่ควรถูกใช้เป็นค่ารับประกันสำหรับ Server ของเรา
㊽ Index คืออะไร
Index ช่วยให้ Database หา Rows ตาม Columns ที่ Query บ่อยได้มีประสิทธิภาพขึ้นในหลาย Use Case
ตัวอย่าง Query ที่ใช้บ่อย:
SELECT id
FROM characters
WHERE identifier = ?
ถ้า identifier เป็น Column ที่ค้นหาบ่อย ควรพิจารณา Index ตาม Schema และ Query Pattern จริง
แต่ไม่ควรสร้าง Index ทุก Column เพราะ Index เองมี Cost ทั้ง Storage และ Write Operations
㊾ ตัวอย่าง Index
CREATE INDEX idx_characters_identifier
ON characters(identifier);
จากนั้น Query:
SELECT id, firstname, lastname
FROM characters
WHERE identifier = ?
สามารถใช้ Index ตาม Query Planner/Database Engine เมื่อเหมาะสม
ควรตรวจด้วย Database Tools จริง ไม่ควรเดาว่า Index ถูกใช้เสมอ
㊿ SELECT * ควรใช้ไหม
ถ้าต้องการเพียง:
id
firstname
lastname
ควรเขียน:
SELECT id, firstname, lastname
FROM characters
WHERE identifier = ?
แทน:
SELECT *
FROM characters
WHERE identifier = ?
เมื่อ Table มี Columns จำนวนมาก การเลือกเฉพาะข้อมูลที่ต้องใช้ช่วยให้ Code ชัดเจนและลดข้อมูลที่ไม่จำเป็นต้องถูกส่งกลับ
51. LIMIT 1 มีประโยชน์เมื่อไหร่
ถ้าต้องการเพียง Row เดียว:
SELECT id
FROM characters
WHERE id = ?
LIMIT 1
แล้วใช้:
MySQL.single.await(...)
ทำให้ Intent ของ Query ชัดเจนว่าคาดหวังหนึ่ง Record.
52. Query Database ทุก Frame ได้ไหม
ไม่ควรสำหรับข้อมูลทั่วไป
ตัวอย่างที่ควรหลีกเลี่ยง:
CreateThread(function()
while true do
local row =
MySQL.single.await(
'SELECT `theme` FROM `settings` WHERE `character_id` = ?',
{
characterId
}
)
Wait(0)
end
end)
Database ไม่ควรถูกใช้เหมือน Runtime Variable
โหลดข้อมูลเมื่อจำเป็นแล้ว Cache/State ตาม Architecture ที่เหมาะสม
53. Database Query ทุกวินาทีดีไหม
ก็ไม่สามารถตอบว่า “ดี” สำหรับทุกระบบ
ถามก่อนว่า:
ข้อมูลเปลี่ยนบ่อยแค่ไหน?
จำเป็นต้องอ่านใหม่หรือไม่?
ใช้ Event ได้ไหม?
Cache ได้ไหม?
State Bag เหมาะกว่าไหม?
ถ้าข้อมูลเปลี่ยนเฉพาะเมื่อ Player กด Save ก็ไม่จำเป็นต้อง Query ทุกวินาที
54. Cache Database Data อย่างไร
ตัวอย่าง:
local characterCache = {}
local function loadCharacter(id)
if characterCache[id] then
return characterCache[id]
end
local row =
MySQL.single.await(
'SELECT `id`, `firstname`, `lastname` FROM `characters` WHERE `id` = ? LIMIT 1',
{
id
}
)
characterCache[id] = row
return row
end
แต่ Cache ต้องมี:
Invalidation
Cleanup
Update
ไม่เช่นนั้น Resource จะอ่านข้อมูลเก่าหรือ Memory โตขึ้นเรื่อยๆ
55. Cache กับ Database ใครเป็น Source of Truth
สำหรับ Persistent Data โดยทั่วไป Database ควรเป็น Source of Truth ระยะยาว
Cache มีไว้ลดการอ่านซ้ำใน Runtime
ตัวอย่าง:
Database
= Persistent Truth
Memory Cache
= Fast Runtime Copy
เมื่อข้อมูลเปลี่ยนต้องกำหนดให้ชัดว่า Update Cache ก่อนหรือ Database ก่อน และหาก Query ล้มเหลวจะ Rollback/Recover อย่างไร
56. Player Disconnect ควร Save ทุกอย่างไหม
ขึ้นกับระบบ
ไม่ควรเก็บข้อมูลสำคัญไว้จนถึง playerDropped เพียงจุดเดียว เพราะ Process สามารถหยุดผิดปกติได้
ระบบสำคัญควรออกแบบ Save Strategy เช่น:
Save เมื่อข้อมูลสำคัญเปลี่ยน
+
Periodic Save ตามความเหมาะสม
+
Save ตอน Disconnect
แต่ไม่ควร Save ทุก Field ทุก Tick
57. Autosave พร้อมกันทุก Player มีปัญหาได้ไหม
ถ้า Server มี Player จำนวนมากแล้ว Resource ทำ:
ทุก 5 นาที
↓
Save Player ทุกคน
↓
Queries จำนวนมากพร้อมกัน
อาจเกิด Load Spike ได้
พิจารณา:
Batching
Staggering
เฉพาะข้อมูลที่ Dirty
Transactions ตาม Use Case
และวัด Query Time จริงก่อนปรับ
58. Dirty Flag คืออะไร
ใช้จำว่าข้อมูลเปลี่ยนจริงหรือยัง
ตัวอย่าง:
Characters[id] = {
data = row,
dirty = false
}
เมื่อมีการเปลี่ยน:
Characters[id].dirty = true
ตอน Autosave:
dirty = true
→ Save
dirty = false
→ Skip
ช่วยลด Query ที่เขียนข้อมูลเดิมซ้ำโดยไม่จำเป็น
59. Resource Start ควร Query Database ทั้งหมดไหม
ไม่จำเป็นสำหรับทุกระบบ
ถ้ามี Database ใหญ่มาก การ:
SELECT *
FROM huge_table
ตอน Resource Start อาจดึงข้อมูลจำนวนมหาศาลที่ไม่จำเป็น
เลือก:
Load เฉพาะ Active Data
Lazy Load
Pagination
Cache เฉพาะที่ใช้
ตาม Use Case
60. Database Schema ควรแยก Table หรือรวม JSON
ไม่มีคำตอบเดียว
ข้อมูลที่ต้อง:
Search
Filter
Sort
Join
Index
มักเหมาะกับ Columns/Tables ที่มี Structure
JSON เหมาะกับข้อมูลบางประเภทที่เป็น Structured Blob และไม่ได้ถูก Query แยก Field บ่อย
อย่าโยนทุกอย่างเข้า JSON เพียงเพราะเขียน Code ง่ายในตอนแรก
61. JSON Column มีข้อควรระวังอะไร
เอกสาร oxmysql เตือนว่าบาง Resources ที่ออกแบบกับ MySQL 5.7 อาจพบ Compatibility Differences กับ MySQL 8 โดยยก LONGTEXT/JSON Default Values เป็นหนึ่งในตัวอย่าง.
ดังนั้น Import SQL จาก Resource เก่าแล้ว Error ควรตรวจ Database Version และ Schema Compatibility ก่อนแก้ SQL แบบสุ่ม
62. Reserved Keyword คืออะไร
บางคำถูก Database ใช้เป็น SQL Keyword
oxmysql ยกตัวอย่างว่า MySQL 8 มี Reserved Keywords เพิ่มขึ้น เช่น stored และ group ซึ่งอาจทำให้ Resources รุ่นเก่าเกิด Compatibility Issues.
วิธีที่ดีคือใช้ชื่อ Column ที่ชัดเจนและหลีกเลี่ยง Reserved Keywords เมื่อออกแบบ Schema ใหม่
63. ทำไม Resource เก่าทำงานกับ MariaDB แต่ MySQL 8 Error
อาจมาจาก SQL Syntax หรือ Schema Compatibility Differences
ไม่ควรสรุปว่า oxmysql พังทันที
ตรวจ:
Database Version
SQL Import
Reserved Keywords
Column Types
JSON/LONGTEXT
Default Values
ก่อน
64. Multiple Statements ควรเปิดไหม
Official oxmysql Common Issues ระบุว่า Multiple Statements ถูกปิดไว้โดย Default เพื่อลดความเสี่ยง SQL Injection จากแนวทางเขียน Query ที่ไม่ปลอดภัย และโครงการแนะนำอย่างชัดเจนว่าไม่ควรเปิดหากไม่จำเป็น.
อย่าแก้ Error ด้วย:
multipleStatements=true
เพียงเพราะต้องการส่ง SQL หลายคำสั่งใน String เดียว
หากต้องการหลาย Operations ที่เป็น Atomic ให้พิจารณา Transaction แทน
65. Client Input ต้อง Validate ก่อน Database Query
ตัวอย่าง Client ส่ง:
TriggerServerEvent(
'character:load',
characterId
)
Server ไม่ควร Query แล้วส่งข้อมูลกลับทันที
ควรตรวจ:
characterId ถูก Format?
↓
Character มีจริง?
↓
Character เป็นของ Player คนนี้?
↓
Player อยู่ State ที่อนุญาต?
↓
จึง Query/Return
Database ไม่ได้ทำหน้าที่เป็น Permission System ให้โดยอัตโนมัติ
66. ตัวอย่าง Server-side Validation
RegisterNetEvent(
'character:request',
function(characterId)
local src = source
if type(characterId) ~= 'number' then
return
end
local identifier =
GetPlayerIdentifierByType(
src,
'license'
)
if not identifier then
return
end
local character =
MySQL.single.await(
[[
SELECT
`id`,
`firstname`,
`lastname`
FROM `characters`
WHERE `id` = ?
AND `identifier` = ?
LIMIT 1
]],
{
characterId,
identifier
}
)
if not character then
return
end
TriggerClientEvent(
'character:receive',
src,
character
)
end
)
หลักสำคัญคือ Server ตรวจทั้ง Character ID และ Player Identifier ก่อนคืน Record
67. อย่าส่ง Database Row ทั้งหมดให้ Client
สมมติ Table มี:
id
identifier
firstname
lastname
internal_notes
admin_flags
private_data
แต่ Client ต้องใช้เพียง:
firstname
lastname
ให้ส่งเฉพาะ Fields ที่จำเป็น
ลดทั้งข้อมูลที่ส่งและการเปิดเผย Internal Server Data โดยไม่จำเป็น
68. SELECT เฉพาะ Columns ช่วยเรื่อง Security ด้วยไหม
ช่วยลด Data Exposure
แทน:
SELECT *
FROM users
WHERE id = ?
ใช้:
SELECT firstname, lastname
FROM users
WHERE id = ?
แล้ว Client จะไม่ได้ข้อมูล Column ที่ Server ไม่จำเป็นต้องส่งตั้งแต่ต้น
69. Database Error ควร Log Password ไหม
ไม่ควร
อย่า Print:
mysql://username:password@host/database
ลง Console, Discord Log หรือ Public Issue
หากต้อง Debug Connection ให้ปิดบัง:
Password
Private Host
Sensitive Credentials
ก่อนแชร์ Log
70. Backup Database สำคัญแค่ไหน
สำคัญมากสำหรับ Server ที่มี Persistent Player Data
อย่างน้อยควรมี:
Automated Backup
Backup Retention
Restore Test
Backup ก่อน Migration
Backup ก่อน Schema Change
Backup ที่ไม่เคยทดสอบ Restore ยังไม่ควรถูกถือว่าปลอดภัยเต็มที่
71. ก่อน Import SQL ใหม่ควรทำอะไร
ทำตามลำดับ:
① Backup
② ตรวจ Resource Version
③ อ่าน Migration Notes
④ ตรวจ Database Version
⑤ Import บน Test Database
⑥ Start Resource
⑦ ตรวจ Errors
⑧ ตรวจ Data
⑨ ค่อยทำ Production
ไม่ควร Import SQL ของ Resource ใหม่ทับ Production Database ทันทีโดยไม่อ่าน Schema
72. ALTER TABLE ควรระวังอะไร
ALTER TABLE สามารถเปลี่ยนโครงสร้าง Database
ตัวอย่าง:
ALTER TABLE characters
ADD COLUMN nickname VARCHAR(50) NULL;
ก่อนทำ Production:
Backup
Test
Check Existing Data
Check Resource Compatibility
โดยเฉพาะ Table ขนาดใหญ่
73. Database Migration คืออะไร
คือกระบวนการเปลี่ยน Schema จาก Version หนึ่งไปอีก Version
ตัวอย่าง:
v1
characters:
id, firstname
↓
v2
characters:
id, firstname, lastname
Resource ที่พัฒนาอย่างเป็นระบบควรรู้ว่า Database Schema Version ไหนใช้กับ Resource Version ไหน
74. Resource Update แล้ว Database Error ควรทำอะไร
ตรวจ:
Resource Release Notes
Migration SQL
Database Schema
Columns
Indexes
Dependencies
Database Engine Version
อย่าเริ่มจากลบ Table แล้ว Import ใหม่ หาก Table มีข้อมูลผู้เล่นจริง
75. FiveM Server Lag ควรวิเคราะห์ Database อย่างไร
ใช้ Workflow:
Server Hitch
↓
FiveM Profiler
↓
Resource ที่เกี่ยวข้อง
↓
oxmysql Slow Query / Debug
↓
หา SQL Query
↓
ตรวจ Index / Schema / Workload
↓
แก้
↓
วัดใหม่
oxmysql ระบุว่าความเร็ว Query ขึ้นกับ Hardware, Database Settings, Version และ Workload และสามารถดู Query Timing ผ่าน Debug Tools ได้.
76. อย่าโทษ Database ทุกครั้งที่ Hitch
ถ้า Query Warning ขึ้นพร้อม Server Hitch อาจเป็นไปได้ว่า Query ดูช้าเพราะ Server เองกำลัง Hitch
oxmysql เตือนตรงๆ ว่า Slow Query Warning ไม่ได้หมายถึง Database Issue เสมอ.
ต้องดูทั้ง:
Profiler
Database Debug
Server CPU
Query Plan
ร่วมกัน
77. Query เร็วแต่ Resource ยังช้าเกิดจากอะไร
อาจเป็น Code หลัง Query เช่น:
Query 5 ms
↓
JSON Decode
↓
Loop 20,000 Rows
↓
Nested Processing
↓
Resource 300 ms
ดังนั้น Database Timing อย่างเดียวไม่เพียงพอ
ต้องดู Resource Profiler ด้วย
78. อย่าดึงข้อมูลหลายหมื่น Row ถ้าใช้เพียง 20
ถ้า UI แสดง 20 Records:
SELECT id, firstname, lastname
FROM characters
ORDER BY id DESC
LIMIT 20;
อาจเหมาะกว่าดึงทั้ง Table แล้วค่อยตัดใน Lua
สำหรับข้อมูลจำนวนมากควรพิจารณา Pagination
79. Pagination คืออะไร
แบ่งข้อมูลออกเป็นหน้า
ตัวอย่าง:
Page 1
Rows 1–20
Page 2
Rows 21–40
แทนการดึง:
50,000 Rows
ครั้งเดียว
ช่วยลดทั้ง Query Result และข้อมูลที่ต้อง Process/ส่งให้ Client
80. Database Connection Error หลังเปลี่ยน Password
ตรวจ Connection String ก่อน
ถ้า Password มี Special Characters เอกสาร oxmysql ระบุว่าบาง Characters อาจเป็น Reserved/Unsupported ขึ้นกับ Connection String Format และแนะนำลองอีก Format เมื่อเกิดปัญหา.
อย่าแชร์ Password จริงเพื่อขอความช่วยเหลือ
81. No such export in resource oxmysql แก้อย่างไร
ตรวจ:
oxmysql Start หรือยัง
↓
ใช้ Release Build หรือไม่
↓
Export Name ถูกหรือไม่
↓
Resource Start Order ถูกหรือไม่
Official Common Issues ระบุสาเหตุหลักเหล่านี้ไว้โดยตรง.
82. Database Server กับ FiveM Server ควรอยู่เครื่องเดียวกันไหม
ไม่มีคำตอบเดียว
เครื่องเดียวกัน:
Setup ง่าย
Latency ต่ำใน Local Host
ใช้ Resource เครื่องเดียวกัน
แยกเครื่อง:
แยก Workload
แยก Failure Domain บางส่วน
ต้องจัด Network/Security เพิ่ม
เลือกตาม Scale และ Infrastructure
สิ่งสำคัญคือ Database Connection ต้องเสถียรและ Query Latency เหมาะสมกับ Workload
83. XAMPP ควรใช้กับ Production FiveM ไหม
oxmysql Documentation ระบุว่า XAMPP เป็น Web Server Stack สำหรับ Development และไม่แนะนำติดตั้ง XAMPP เพียงเพื่อรัน Database โดยแนะนำติดตั้ง MariaDB โดยตรงแทน.
ดังนั้น Production Database ควรถูกติดตั้งและดูแลเป็น Database Service อย่างเหมาะสม
84. Database Username ควรใช้ root ไหม
สำหรับ Production ควรพิจารณา Dedicated Database User ที่มี Permission เท่าที่ Resource ต้องใช้ แทนใช้ Administrative Account กว้างเกินไป
แนวคิด:
fivem_app
↓
Permission เฉพาะ Database ที่ต้องใช้
ช่วยลดผลกระทบหาก Credential ของ Application ถูกเปิดเผย
85. ควรเปิด Database ให้ Internet ทั้งหมดเข้าถึงไหม
ไม่ควรหากไม่จำเป็น
Database ควรรับ Connection เฉพาะจากระบบที่ต้องใช้งาน เช่น FiveM Server หรือ Management Host ที่กำหนด
ใช้ Network Firewall และ Database User Permissions ร่วมกันตาม Infrastructure
86. Checklist FiveM Database
ก่อน Production ตรวจ:
Database Server ทำงาน
Database ถูกสร้างแล้ว
ใช้ Database Engine ที่ Resource รองรับ
oxmysql/Wrapper ถูก Version
Connection String ถูกต้อง
ไม่เปิดเผย Password
oxmysql Start ก่อน Resource
Resource Dependency ถูก
SQL Schema Import ครบ
Primary Keys ถูก
Indexes สำคัญมีครบ
ใช้ Parameter Placeholders
ไม่ต่อ User Input เข้า SQL String
ใช้
queryเมื่อหลาย Rowsใช้
singleเมื่อหนึ่ง Rowใช้
scalarเมื่อหนึ่งค่าใช้
insertเมื่อ Insertใช้
updateเมื่อ Updateใช้ Transaction เมื่อ Operations ต้องสำเร็จพร้อมกัน
ใช้ Prepare กับ Frequently-called Queries เมื่อเหมาะสม
ไม่ Query ทุก Frame
ไม่ SELECT
*โดยไม่จำเป็นใช้ LIMIT/Pagination เมื่อข้อมูลเยอะ
Cache เมื่อเหมาะสม
Cache มี Cleanup
Validate Client Input
ตรวจ Record Ownership
ไม่ส่ง Private Columns ให้ Client
ตรวจ Slow Query Warnings
ใช้ Profiler ร่วมกับ Database Debug
Backup Database
ทดสอบ Restore
Backup ก่อน Migration
Test Schema Changes
ไม่ใช้ NetID เป็น Persistent ID
ไม่ใช้ Server ID เป็น Persistent ID
แยก Runtime State กับ Persistent State
ตรวจ Resource Restart
ตรวจ Server Restart
ตรวจ Player Disconnect
ตาราง oxmysql Functions ที่ควรรู้
| Function | เหมาะกับ |
|---|---|
MySQL.query | หลาย Rows / Query ทั่วไป |
MySQL.single | Row เดียว |
MySQL.scalar | ค่าเดียว |
MySQL.insert | Insert และรับ Insert ID |
MySQL.update | Update และรับจำนวน Rows ที่ได้รับผล |
MySQL.prepare | Query ที่เรียกบ่อย |
MySQL.transaction | หลาย Query ที่ต้องสำเร็จทั้งหมด |
Functions กลุ่มนี้ถูกระบุใน oxmysql API Documentation ปัจจุบัน.
ตัวอย่าง Resource Database แบบพื้นฐาน
fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
server_script 'server.lua'
Cfx.re ระบุว่า server_script ใช้โหลด Script ฝั่ง Server.
server.lua
RegisterCommand(
'myprofile',
function(source)
if source == 0 then
return
end
local identifier =
GetPlayerIdentifierByType(
source,
'license'
)
if not identifier then
return
end
local profile =
MySQL.single.await(
[[
SELECT
`id`,
`display_name`
FROM `profiles`
WHERE `identifier` = ?
LIMIT 1
]],
{
identifier
}
)
if not profile then
print(
('No profile for %s')
:format(identifier)
)
return
end
print(
('Profile id=%s name=%s')
:format(
profile.id,
profile.display_name
)
)
end,
false
)
ตัวอย่างนี้ใช้ Parameter Placeholder และ MySQL.single.await() ตาม API ของ oxmysql.
ตัวอย่าง Insert Profile
local profileId =
MySQL.insert.await(
[[
INSERT INTO `profiles`
(`identifier`, `display_name`)
VALUES (?, ?)
]],
{
identifier,
displayName
}
)
if not profileId then
return
end
print(
('Profile created: %s')
:format(profileId)
)
MySQL.insert คืน Insert ID ของ Row ที่เพิ่มเมื่อมีค่าที่เหมาะสม.
ตัวอย่าง Update Profile
local affected =
MySQL.update.await(
[[
UPDATE `profiles`
SET `display_name` = ?
WHERE `id` = ?
]],
{
newDisplayName,
profileId
}
)
print(
('Affected rows: %s')
:format(affected)
)
MySQL.update คืนจำนวน Rows ที่ได้รับผล.
ตัวอย่างอ่านหลาย Characters
local characters =
MySQL.query.await(
[[
SELECT
`id`,
`firstname`,
`lastname`
FROM `characters`
WHERE `identifier` = ?
ORDER BY `id` ASC
]],
{
identifier
}
)
for i = 1, #characters do
local character =
characters[i]
print(
character.id,
character.firstname,
character.lastname
)
end
MySQL.query เหมาะกับ Result หลาย Rows.
FiveM Database ต่อไม่ได้ แก้อย่างไร
ตรวจตามลำดับ:
Database Running?
↓
Database Name ถูก?
↓
User/Password ถูก?
↓
Host/Port ถูก?
↓
Connection String ถูก?
↓
Special Character Problem?
↓
oxmysql Started?
↓
Resource Order ถูก?
Official oxmysql Common Issues ให้ตรวจ Connection String, Database State และ Start Order เป็นจุดแรกๆ.
FiveM oxmysql Slow Query แก้อย่างไร
อย่าแก้ด้วยการเพิ่ม Threshold เพื่อซ่อน Warning ทันที
ตรวจ:
Query Text
↓
จำนวน Rows
↓
WHERE Condition
↓
Index
↓
Database Load
↓
Server Hitch
↓
Profiler
เพราะ oxmysql เตือนว่า Slow Warning อาจไม่ได้เกิดจาก Database เพียงอย่างเดียว.
FiveM Database ทำ Server Lag ไหม
Database สามารถเป็นส่วนหนึ่งของปัญหาได้หาก Resource ใช้ Queries จำนวนมากหรือ Query ช้า แต่ต้องวัดจริง
ใช้:
FiveM Profiler
+
oxmysql Debug
+
Slow Query Warnings
ประกอบกัน เพราะ Query Speed ขึ้นกับ Hardware, Settings, Version และ Workload.
FiveM ควรใช้ MySQL หรือ MariaDB
ให้ยึด Compatibility ของ Resources เป็นหลัก
oxmysql Documentation ปัจจุบันแนะนำ MariaDB สำหรับ Compatibility และระบุว่า FiveM Resources รุ่นเก่าบางตัวอาจเจอปัญหากับ MySQL 8.
หาก Server ที่ทำงานอยู่มี Database Engine อยู่แล้ว อย่า Migration เพียงเพราะคำแนะนำหนึ่งบรรทัดโดยไม่ทดสอบ Schema และ Resources ทั้งหมด
FiveM Database ควร Backup บ่อยแค่ไหน
ขึ้นกับมูลค่าและอัตราการเปลี่ยนแปลงข้อมูล
Server ที่มี Player Data เปลี่ยนตลอดควรมี Automated Backup Schedule ที่เหมาะสมและ Retention หลายชุด
สิ่งสำคัญกว่า “สำรองบ่อยแค่ไหน” คือ:
Backup สำเร็จจริง?
Restore ได้จริง?
มี Copy ก่อน Migration?
FiveM Database หายหลัง Resource Restart ได้ไหม
ข้อมูล Database ปกติควรยังอยู่
แต่ Runtime Cache:
local cache = {}
จะถูก Reset
Resource จึงต้องสามารถ Reload Persistent Data กลับจาก Database เมื่อ Start ใหม่ตาม Architecture
FiveM Database หายหลัง Server Restart ได้ไหม
ถ้า Database Server และ Storage ทำงานปกติ Records ควรคงอยู่
ถ้าข้อมูล “หาย” ให้ตรวจว่า:
Resource Save จริงหรือไม่
Transaction Commit หรือไม่
ใช้ Database ถูกตัวหรือไม่
Connection String เปลี่ยนหรือไม่
Table ถูก Reset/Truncate หรือไม่
Migration Script ทำอะไร
อย่าเริ่มสร้างข้อมูลใหม่ทับก่อนตรวจ Backup
FAQ FiveM Database
FiveM Database คืออะไร
เป็น Persistent Storage สำหรับข้อมูลที่ Resources ต้องใช้ข้าม Resource Restart, Player Disconnect หรือ Server Restart เช่น Profiles, Characters และ Vehicle Records
FiveM ต้องใช้ MySQL ทุก Serverไหม
ไม่ ตัว FXServer ไม่ได้บังคับให้ทุก Server ต้องใช้ MySQL และ txAdmin เองมี Player Database ที่ระบุว่าไม่ต้องใช้ MySQL แต่ Resources/Frameworks หลายตัวมี Database Dependencies ของตัวเอง.
oxmysql คืออะไร
เป็น FiveM Resource สำหรับสื่อสารกับ MySQL Database โดยใช้ node-mysql2 และมี API สำหรับ Query Operations หลายประเภท.
oxmysql ควร Start ตอนไหน
ควร Start ก่อน Resources ที่ต้องเรียก oxmysql และ Connection String ต้องถูกตั้งไว้ก่อน Resources เหล่านั้นเริ่ม.
MySQL.query ใช้ทำอะไร
ใช้ Execute Query และสำหรับ SELECT จะคืน Matching Rows และ Columns.
MySQL.single ใช้ทำอะไร
ใช้เมื่อคาดหวัง Result เพียงหนึ่ง Row.
MySQL.insert คืนอะไร
คืน Insert ID ของ Row ที่เพิ่มเมื่อมีค่าที่ถูกต้อง.
MySQL.update คืนอะไร
คืนจำนวน Rows ที่ได้รับผลจาก Update Query.
MySQL.transaction คืออะไร
Execute หลาย Queries และ Commit เมื่อทั้งหมดสำเร็จ หากหนึ่ง Query ล้มเหลวจะไม่ Commit ชุดนั้น.
Parameter Placeholder สำคัญไหม
สำคัญ เพราะช่วยแยก Parameter Values ออกจาก SQL และ oxmysql ระบุว่า Placeholders ช่วยป้องกัน SQL Injection วิธีทั่วไป.
FiveM Slow Query Warning คืออะไร
oxmysql สามารถเตือนเมื่อ Query ใช้เวลานานกว่า Threshold ที่กำหนด แต่ Warning ไม่ได้หมายความว่า Database มีปัญหาเสมอ เพราะ Server Hitch ก็มีผลต่อ Query Timing ได้.
FiveM Database ใช้ XAMPP ดีไหม
สำหรับ oxmysql เอกสารไม่แนะนำติดตั้ง XAMPP เพียงเพื่อใช้ Database และแนะนำติดตั้ง MariaDB โดยตรงแทน.
ประเด็นสำคัญ
FiveM Database มีหน้าที่หลักคือเก็บ Persistent Data ส่วนข้อมูลอย่าง Entity Handle, NetID, Current Owner หรือ Temporary Session เป็น Runtime Data และไม่ควรถูกนำมาใช้เป็น Persistent Identity โดยตรง
หาก Resource ใช้ oxmysql โครงสร้างที่ควรเข้าใจคือ:
FiveM Server Resource
↓
Server-side Lua / JS
↓
oxmysql
↓
MySQL / MariaDB
↓
Persistent Data
oxmysql รองรับ query, single, scalar, insert, update, prepare และ transaction โดยแต่ละ Function มีจุดประสงค์แตกต่างกัน และ Transaction เหมาะกับ Operations ที่ต้องสำเร็จทั้งหมดเป็นชุดเดียว.
Resource ใหม่ควรใช้ Parameter Placeholders เช่น ? แทนการต่อ User Input เข้า SQL String โดยตรง และ oxmysql Documentation ระบุว่า Placeholder ช่วยลด SQL Injection Methods ทั่วไป ขณะที่ Named Placeholders ถูกระบุว่า Deprecated.
ด้าน Performance อย่า Query Database ทุก Frame และอย่าโทษ Database ทันทีเมื่อพบ Slow Query Warning ให้ใช้ FiveM Profiler ร่วมกับ oxmysql Debug/Query Timing เพราะ oxmysql ระบุว่า Query Performance ขึ้นกับ Hardware, Database Settings, Database Version และ Current Workload.
สำหรับผู้อ่าน comsiam ให้จำสูตร “Database = Persistent, Cache = Runtime, State Bag = Network State” และ comsiam แนะนำให้ Resource ทุกตัวแยก Data 3 กลุ่มนี้ให้ชัด ใช้ Parameterized Queries, Validate Client Requests, ทำ Backup และวัด Query Performance ก่อน Optimize จะลดทั้งปัญหาข้อมูลหาย Query ช้า และ Server Hitch ได้มาก
Comments
Post a Comment