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 ระบุขั้นตอนหลักคือ:

  1. ติดตั้ง Database Server

  2. ดาวน์โหลด Release ของ oxmysql

  3. ให้ oxmysql Start ก่อน Resources ที่ต้องเรียก Database

  4. ตั้ง 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 ตรวจ:

  1. Database Server ทำงาน

  2. Database ถูกสร้างแล้ว

  3. ใช้ Database Engine ที่ Resource รองรับ

  4. oxmysql/Wrapper ถูก Version

  5. Connection String ถูกต้อง

  6. ไม่เปิดเผย Password

  7. oxmysql Start ก่อน Resource

  8. Resource Dependency ถูก

  9. SQL Schema Import ครบ

  10. Primary Keys ถูก

  11. Indexes สำคัญมีครบ

  12. ใช้ Parameter Placeholders

  13. ไม่ต่อ User Input เข้า SQL String

  14. ใช้ query เมื่อหลาย Rows

  15. ใช้ single เมื่อหนึ่ง Row

  16. ใช้ scalar เมื่อหนึ่งค่า

  17. ใช้ insert เมื่อ Insert

  18. ใช้ update เมื่อ Update

  19. ใช้ Transaction เมื่อ Operations ต้องสำเร็จพร้อมกัน

  20. ใช้ Prepare กับ Frequently-called Queries เมื่อเหมาะสม

  21. ไม่ Query ทุก Frame

  22. ไม่ SELECT * โดยไม่จำเป็น

  23. ใช้ LIMIT/Pagination เมื่อข้อมูลเยอะ

  24. Cache เมื่อเหมาะสม

  25. Cache มี Cleanup

  26. Validate Client Input

  27. ตรวจ Record Ownership

  28. ไม่ส่ง Private Columns ให้ Client

  29. ตรวจ Slow Query Warnings

  30. ใช้ Profiler ร่วมกับ Database Debug

  31. Backup Database

  32. ทดสอบ Restore

  33. Backup ก่อน Migration

  34. Test Schema Changes

  35. ไม่ใช้ NetID เป็น Persistent ID

  36. ไม่ใช้ Server ID เป็น Persistent ID

  37. แยก Runtime State กับ Persistent State

  38. ตรวจ Resource Restart

  39. ตรวจ Server Restart

  40. ตรวจ Player Disconnect

ตาราง oxmysql Functions ที่ควรรู้

Functionเหมาะกับ
MySQL.queryหลาย Rows / Query ทั่วไป
MySQL.singleRow เดียว
MySQL.scalarค่าเดียว
MySQL.insertInsert และรับ Insert ID
MySQL.updateUpdate และรับจำนวน Rows ที่ได้รับผล
MySQL.prepareQuery ที่เรียกบ่อย
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

Popular posts from this blog

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

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

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