FiveM SQL Error และ Slow Query แก้อย่างไร? วิธีแก้ Database Error และ Query ช้าใน oxmysql
ปัญหา FiveM SQL Error และ Slow Query ต้องแยกออกเป็น 2 กลุ่มใหญ่ คือ Query ผิดจน Database ปฏิเสธการทำงาน กับ Query ทำงานได้แต่ช้าเกินไป เพราะแนวทางแก้ต่างกันโดยสิ้นเชิง
ตัวอย่าง SQL Error ที่พบบ่อย
Unknown column
Table doesn't exist
Duplicate entry
SQL syntax error
Data too long
Column cannot be null
Lock wait timeout
Deadlock
ส่วนปัญหา Slow Query มักเกี่ยวกับ
ไม่มี Index
Query Rows มากเกินไป
SELECT *
N+1 Query
Query ใน Loop
JOIN ไม่เหมาะสม
ORDER BY / GROUP BY หนัก
LIKE '%keyword%'
Transaction ค้างนาน
Lock
Database Load
Server Hitch
ลำดับ Debug ที่เหมาะคือ
SQL Error?
↓
แก้ Error ก่อน
↓
Query ทำงานแล้ว?
↓
วัดเวลา
↓
ดู Slow Query
↓
EXPLAIN
↓
ตรวจ Index
↓
ลด Rows / Queries
↓
ตรวจ Lock / Transaction
↓
วัดผลอีกครั้ง
อย่าแก้ Query ช้าด้วยการเพิ่ม RAM หรือ CPU ก่อนตรวจ SQL และ Index เพราะ Query ที่ออกแบบผิดสามารถช้าได้แม้ Database Server แรงมาก
① FiveM SQL Error คืออะไร
SQL Error คือ Database ไม่สามารถ Execute คำสั่งตามที่ Resource ส่งมาได้
ตัวอย่าง
SELECT money
FROM characters
WHERE character_id = ?;
ถ้า Table ไม่มี money
อาจพบ Error
Unknown column 'money'
หรือถ้า Table characters ไม่มี
อาจพบ
Table 'database.characters' doesn't exist
ตรงนี้ปัญหาไม่ใช่ oxmysql ต่อ Database ไม่ได้
แต่เป็น Schema หรือ SQL Query ไม่ตรงกัน
② Slow Query คืออะไร
Slow Query คือ Query ที่ Execute สำเร็จแต่ใช้เวลานานเกิน Threshold
oxmysql ปัจจุบันมีค่า
mysql_slow_query_warning
สำหรับแจ้งเตือน Query ที่ใช้เวลานาน
ตัวอย่าง Configuration
set mysql_slow_query_warning 150
หมายถึงให้ oxmysql แจ้ง Query ที่ช้าเกิน Threshold ที่กำหนด
แต่ Warning ไม่ได้แปลว่า Database มีปัญหาเสมอไป เพราะ Server Hitch หรือ Startup Load ก็มีผลต่อเวลาที่รายงานได้
③ SQL Error กับ Slow Query ต่างกันอย่างไร
SQL Error
→ Query ทำงานไม่สำเร็จ
เช่น
Unknown column
Duplicate entry
Syntax error
ส่วน
Slow Query
→ Query สำเร็จ
→ แต่ใช้เวลานาน
เช่น
SELECT vehicles
→ 800 ms
อย่าเอาวิธีแก้ของสองกลุ่มมาปนกัน
④ จุดแรกที่ต้องดูคือ FXServer Console
เมื่อ oxmysql พบ Error มักมีรายละเอียด Query และ Database Error
เช่น
Unknown column 'garage' in 'field list'
ให้ดู
Resource ไหนยิง Query?
SQL อะไร?
Table ไหน?
Column ไหน?
Error Code อะไร?
อย่าแก้ Query อื่นที่ไม่เกี่ยวข้อง
⑤ เปิด mysql_debug ได้ไหม
ได้ในช่วง Debug
ตัวอย่าง
set mysql_debug true
oxmysql สามารถแสดง Query ใน Console เพื่อช่วยตรวจว่า Resource ส่ง SQL อะไรจริง
แต่ Production Server ไม่จำเป็นต้องเปิด Query Debug จำนวนมากตลอดเวลา
เพราะจะสร้าง Log Noise และข้อมูลบางอย่างอาจมีความอ่อนไหว
⑥ Unknown column คืออะไร
MariaDB Error Code 1054 หมายถึง
Unknown column
ตัวอย่าง
SELECT garage
FROM vehicles;
แต่ Table จริงมีเพียง
id
plate
model
stored
Database จึงหา garage ไม่เจอ
⑦ Unknown column แก้อย่างไร
ตรวจ
DESCRIBE vehicles;
หรือ
SHOW CREATE TABLE vehicles;
แล้วเปรียบเทียบกับ Query ที่ Resource ใช้
สาเหตุพบบ่อยคือ
SQL Migration ยังไม่ Import
Script ใหม่กว่า Database Schema
Database Schema ใหม่กว่า Script
สะกด Column ผิด
เลือก Database ผิด
⑧ อย่าเพิ่ม Column แบบเดาสุ่ม
ถ้า Resource แจ้ง
Unknown column 'garage'
ไม่ควรรีบทำ
ALTER TABLE vehicles
ADD garage VARCHAR(100);
โดยยังไม่อ่าน SQL/Migration ของ Resource
เพราะ Column อาจต้องเป็น Type อื่น หรือมี Index/Default/Constraint เพิ่ม
ควรใช้ Migration ที่ตรง Version
⑨ Table doesn't exist คืออะไร
MariaDB Error 1146
Table doesn't exist
ตัวอย่าง
SELECT *
FROM owned_vehicles;
แต่ Database ไม่มี Table owned_vehicles
ปัญหานี้มักเกิดจาก
ยังไม่ได้ Import SQL
ใช้ Framework คนละ Version
เลือก Database ผิด
Resource ต้อง Migration เพิ่ม
⑩ วิธีตรวจ Table
ใช้
SHOW TABLES;
หรือ
SHOW TABLES LIKE 'owned_vehicles';
ถ้าไม่มีจริง ให้หา SQL Installation/Migration ของ Resource
อย่าสร้าง Table เปล่าขึ้นมาเพียงเพื่อให้ Error หาย
⑪ SQL Syntax Error คืออะไร
MariaDB Error กลุ่ม 1064 มักเกิดจาก SQL Syntax ไม่ถูกต้อง
เช่น
SELECT *
FROM vehicles
WHER id = 1;
คำว่า
WHER
ผิด
ต้องเป็น
WHERE
⑫ Syntax Error อาจมาจาก Reserved Word
เช่นตั้ง Column หรือ Table ด้วยคำที่ Database Version มองเป็น Reserved Keyword
ตัวอย่างแนวคิด
SELECT group
FROM players;
อาจต้องใช้ชื่ออื่นหรือ Quote Identifier อย่างถูกต้อง
เช่น
SELECT `group`
FROM players;
แต่ในระยะยาวการใช้ชื่อที่ไม่ชน Reserved Keywords มัก Maintain ง่ายกว่า
⑬ SQL_MODE ก็ทำให้ Syntax ต่างกันได้
SQL บางแบบทำงานภายใต้ SQL Mode หนึ่ง แต่ไม่ทำงานในอีก Mode
ดังนั้น Script เก่าที่เคยทำงานบน Database รุ่นเก่าอาจ Error หลังย้าย Server
ตรวจ
SELECT @@sql_mode;
แต่ไม่ควรปิด Strict Mode ทั้ง Server เพียงเพื่อให้ Script เก่าใช้ได้โดยไม่ตรวจผลกระทบ
แก้ Schema/SQL ให้ถูกต้องมักเป็นแนวทางที่ดีกว่า
⑭ Duplicate entry คืออะไร
MariaDB Error 1062
Duplicate entry
เกิดเมื่อพยายาม Insert ค่าซ้ำเข้า Column ที่มี
PRIMARY KEY
หรือ
UNIQUE INDEX
ตัวอย่าง
plate = ABC123
มีอยู่แล้ว
แล้ว Script พยายาม Insert ABC123 ซ้ำใน Column ที่ Unique
⑮ ตัวอย่าง Duplicate Entry
Table
CREATE TABLE vehicles (
id INT AUTO_INCREMENT PRIMARY KEY,
plate VARCHAR(20) UNIQUE
);
จากนั้น
INSERT INTO vehicles (plate)
VALUES ('ABC123');
ครั้งแรกผ่าน
แต่ทำซ้ำ
INSERT INTO vehicles (plate)
VALUES ('ABC123');
จะเกิด Duplicate Entry
⑯ Duplicate Entry แก้อย่างไร
ต้องถามก่อนว่า ข้อมูลควร Unique จริงหรือไม่
ถ้า Plate ต้องไม่ซ้ำ
ปัญหาอยู่ที่ Application Logic
Server ควรตรวจ/สร้าง Plate ใหม่
ไม่ใช่ลบ Unique Index ออกเพียงเพื่อให้ Error หาย
⑰ INSERT IGNORE ควรใช้ไหม
MariaDB รองรับ
INSERT IGNORE
ซึ่งสามารถเปลี่ยน Duplicate บางกรณีเป็น Warning
แต่ไม่ควรใช้เพื่อซ่อน Bug
ถ้าระบบต้องรู้ว่า Vehicle ถูก Insert หรือไม่ ควรจัด Duplicate Logic ให้ชัดเจน
⑱ ON DUPLICATE KEY UPDATE ใช้เมื่อไร
เหมาะเมื่อเจตนาคือ
ไม่มี Row
→ Insert
มี Row อยู่แล้ว
→ Update
ตัวอย่าง
INSERT INTO settings (
character_id,
volume
)
VALUES (?, ?)
ON DUPLICATE KEY UPDATE
volume = VALUES(volume);
เหมาะกับ Setting หรือ Upsert Pattern
ไม่เหมาะกับข้อมูลที่ Duplicate แสดงถึง Bug
⑲ Column cannot be null คืออะไร
เกิดเมื่อ Column ถูกกำหนด
NOT NULL
แต่ Script ส่ง NULL
ตัวอย่าง
INSERT INTO characters (
firstname
)
VALUES (NULL);
ถ้า firstname ห้าม Null Database จะปฏิเสธ
⑳ แก้ NULL Error อย่างไร
ตรวจว่า
ค่าหายจากไหน?
Client ไม่ส่ง?
Server Validation ไม่ครบ?
Framework API คืน nil?
Database Default ไม่มี?
อย่าแก้ด้วยการเปลี่ยนทุก Column ให้ NULL ได้
ถ้า Field จำเป็นจริงก็ควรบังคับให้ Application ส่งข้อมูลที่ถูกต้อง
㉑ Field doesn't have a default value
เกิดเมื่อ Insert ไม่ได้ส่ง Column ที่
NOT NULL
และไม่มี Default
เช่น Table
cash INT NOT NULL
แต่ Insert
INSERT INTO characters (
firstname
)
VALUES (?);
โดยไม่มี cash
อาจ Error ตาม SQL Mode
㉒ วิธีแก้ Default Value
เลือกตาม Domain
เช่น Cash เริ่มต้นเป็น 0
cash INT NOT NULL DEFAULT 0
หรือให้ Application ส่งค่าเริ่มต้นอย่างชัดเจน
อย่ากำหนด Default แบบสุ่มให้ Field ที่มี Business Meaning สำคัญ
㉓ Data too long คืออะไร
เกิดเมื่อข้อมูลยาวเกิน Column ที่รองรับ
เช่น
name VARCHAR(20)
แต่ Script ส่ง String ยาว 100 ตัว
หรือ JSON ใหญ่เกิน Type ที่กำหนด
ควรตรวจ
Data Type
Maximum Length
Application Validation
Real-world Requirements
㉔ อย่าเพิ่ม VARCHAR เป็น 10000 ทุก Column
แม้ดูเหมือนแก้ง่าย แต่ Database Schema ควรเหมาะกับข้อมูลจริง
เช่น
Plate
→ VARCHAR(20)
Character name
→ VARCHAR(50)
JSON metadata
→ JSON/LONGTEXT ตาม Compatibility
เลือก Type ให้ตรง Use Case
㉕ Incorrect value / Data Type Error
ตัวอย่าง Script ส่ง
abc
เข้า Column
INT
หรือ Timestamp Format ผิด
ให้ตรวจ Lua/JS Parameter ก่อน Query
Parameterized Query ป้องกัน SQL Injection แต่ไม่ได้แปลง Business Data ให้ถูก Type ทุกกรณี
㉖ Foreign Key Error คืออะไร
ถ้า Schema ใช้ Foreign Key
เช่น
vehicles.character_id
→ characters.id
แล้ว Insert Vehicle ให้ Character ID ที่ไม่มีจริง
Database สามารถปฏิเสธได้
นี่เป็นเรื่อง Data Integrity ไม่ใช่ oxmysql Error
㉗ Foreign Key ดีไหม
มีข้อดีด้าน Referential Integrity
แต่ FiveM Schemas หลาย Framework ไม่ได้ใช้ Foreign Keys หนักทุกส่วน
ถ้าใช้ต้องเข้าใจ
Delete behavior
Cascade
Indexes
Migration
Transaction
ไม่ควรเพิ่มเพียงเพราะ Database Design ดูเป็นมาตรฐาน
㉘ Lock wait timeout คืออะไร
MariaDB Error 1205
Lock wait timeout exceeded
หมายถึง Transaction รอ Lock นานเกินกำหนด
อาจเกิดเมื่อ Transaction อื่นถือ Row/Table Lock ไว้นาน
ต้องดู
Transaction ไหนเปิดอยู่?
Query ไหน Lock Rows?
Transaction ยาวเกินไปหรือไม่?
㉙ Deadlock คืออะไร
MariaDB Error 1213
Deadlock found when trying to get lock
ตัวอย่าง
Transaction A
ล็อก Row 1
แล้วรอ Row 2
Transaction B
ล็อก Row 2
แล้วรอ Row 1
ทั้งสองรอกันเอง
Database ต้องเลือก Transaction หนึ่งให้ Fail เพื่อแก้ Deadlock
㉚ Deadlock แปลว่า Database พังไหม
ไม่
Deadlock เป็นสถานการณ์ที่เกิดขึ้นได้ในระบบ Concurrent
สิ่งสำคัญคือ Application ควร
Transaction สั้น
Lock Rows ในลำดับคงที่
Retry Operation ที่เหมาะสม
และไม่ทำ Work ที่ไม่เกี่ยวข้องค้างอยู่ใน Transaction นาน ๆ
㉛ Transaction ไม่ควรเปิดไว้นาน
ตัวอย่างไม่ดี
BEGIN
↓
Update player
↓
รอ HTTP API
↓
Wait 5 วินาที
↓
Update business
↓
COMMIT
ระหว่างนั้น Locks อาจถูกถือไว้นาน
ควรเตรียมข้อมูลที่จำเป็นก่อน แล้วทำ Transaction ให้สั้นที่สุด
㉜ Slow Query Warning ใน oxmysql
oxmysql มี Threshold ปัจจุบันผ่าน Convar
set mysql_slow_query_warning 150
Source ปัจจุบันกำหนด Default เป็นประมาณ
200 ms
ถ้าไม่ได้ Override
จึงสามารถใช้ Warning เป็นสัญญาณว่า Query ใดควรตรวจเพิ่มเติม
㉝ Slow Query Warning เชื่อได้ 100% ไหม
ไม่
เอกสาร oxmysql ระบุว่า Query Time อาจไม่แม่นทั้งหมด และ Server Hitch สามารถทำให้ Query ดูช้า
Startup Query ที่ช้าครั้งเดียวก็อาจไม่ใช่ปัญหาสำคัญ
ดังนั้นต้องดู Pattern
ช้าบ่อยไหม?
เกิดตอนผู้เล่นเยอะไหม?
Rows examined เท่าไร?
Index ใช้ไหม?
㉞ Slow Query เริ่มแก้จากอะไร
อย่าเริ่มด้วยการเขียน Query ใหม่ทันที
ใช้
EXPLAIN
SELECT ...
ก่อน
เช่น
EXPLAIN
SELECT id, plate
FROM vehicles
WHERE character_id = 100;
Database จะบอก Execution Plan ว่ากำลังเข้าถึง Table อย่างไร
㉟ EXPLAIN ใช้ดูอะไร
สิ่งที่ควรสนใจ เช่น
type
possible_keys
key
rows
Extra
ถ้า Query ตรวจ Rows จำนวนมากทั้ง Table และไม่มี Key ถูกใช้ นั่นเป็นสัญญาณให้ตรวจ Index
㊱ Full Table Scan คืออะไร
สมมติ Table มี
1,000,000 rows
แต่ Query ต้องการรถของ Character คนเดียว
SELECT *
FROM vehicles
WHERE character_id = ?;
ถ้าไม่มี Index ที่เหมาะ Database อาจต้องตรวจ Rows จำนวนมาก
นี่คือหนึ่งในสาเหตุ Query ช้าที่พบบ่อย
㊲ Index คืออะไร
Index คือโครงสร้างที่ช่วย Database หา Rows ตาม Column ได้เร็วขึ้น
เช่น Query หลักคือ
SELECT id, plate, model
FROM vehicles
WHERE character_id = ?;
อาจพิจารณา Index
CREATE INDEX idx_vehicles_character_id
ON vehicles(character_id);
แต่ต้องวัดและตรวจ EXPLAIN
㊳ อย่าสร้าง Index ทุก Column
MariaDB แนะนำให้ Index ตาม Query Patterns ที่ Application ใช้จริง
เพราะ Index เพิ่ม Cost ให้
INSERT
UPDATE
DELETE
Storage
ยิ่งมี Index เยอะ Write Operations ก็ยิ่งต้อง Update Structures หลายชุด
㊴ Composite Index คืออะไร
คือ Index หลาย Columns
เช่น Query
SELECT id
FROM vehicles
WHERE character_id = ?
AND stored = ?;
อาจพิจารณา
CREATE INDEX idx_vehicle_owner_stored
ON vehicles(character_id, stored);
แต่ Order ของ Columns สำคัญและต้องอิง Query จริง
㊵ Index Column Boolean อย่างเดียวดีไหม
เช่น
stored = 0/1
มีค่าเพียงไม่กี่แบบ
Index stored เพียงตัวเดียวอาจ Selectivity ต่ำมาก
แต่
(character_id, stored)
อาจเหมาะกว่าใน Query ของ Garage
ต้องตรวจ EXPLAIN ไม่ใช่เดา
㊶ SELECT * ทำให้ Query ช้าไหม
ไม่เสมอ แต่สามารถส่งข้อมูลมากเกินจำเป็น
ถ้าต้องใช้เพียง
id
plate
stored
ควรเขียน
SELECT id, plate, stored
FROM vehicles
WHERE character_id = ?;
แทน
SELECT *
โดยเฉพาะ Table ที่มี JSON/LONGTEXT ขนาดใหญ่
㊷ LIMIT ใช้ช่วยได้ไหม
ถ้าต้องการเพียง Row เดียว
SELECT id, cash
FROM characters
WHERE id = ?
LIMIT 1;
ทำ Intent ชัดเจน
แต่ LIMIT 1 ไม่ได้แทน Index
ถ้า WHERE ไม่มี Index Database อาจยังตรวจข้อมูลจำนวนมากก่อนพบ Row
㊸ N+1 Query คืออะไร
ตัวอย่าง
Query 1
โหลดผู้เล่น 100 คน
จากนั้น
Query 100 ครั้ง
โหลดรถทีละคน
รวมเป็น 101 Queries
อาจแก้ด้วย
JOIN
IN (...)
Batch Query
Cache
ตาม Architecture
㊹ ตัวอย่าง N+1 ที่ควรหลีกเลี่ยง
for i = 1, #players do
local vehicles =
MySQL.query.await(
'SELECT * FROM vehicles WHERE character_id = ?',
{
players[i].id
}
)
end
Player 200 คนก็อาจเกิด 200 Queries
ถ้าต้องใช้พร้อมกันสามารถออกแบบ Batch Query แทน
㊺ Query ใน Loop อันตรายเสมอไหม
ไม่เสมอ
Loop 3 ครั้งตอน Admin Command อาจไม่มีปัญหา
แต่
200 Players
×
ทุก 1 วินาที
ต่างกันมาก
ต้องวัด
Frequency
Rows
Concurrent Players
Query Cost
ก่อนตัดสิน
㊻ อย่า Query Database ทุก Frame
ไม่ควรทำ
CreateThread(function()
while true do
Wait(0)
MySQL.query.await(
'SELECT * FROM characters'
)
end
end)
Database ไม่ใช่ระบบ Frame State
ใช้ Cache/Event-driven Logic แทน
㊼ Runtime Cache ช่วยอย่างไร
ตัวอย่าง
Player Login
↓
SELECT Character
↓
Cache[source]
↓
Gameplay อ่าน Cache
↓
Save เมื่อจำเป็น
ช่วยลด Database Reads ซ้ำ
แต่ต้องมี Save Strategy ที่ปลอดภัย
㊽ Cache ทุกอย่างดีไหม
ไม่
Cache มากเกินไปสร้างปัญหา
Stale Data
Memory Usage
Synchronization
Restart Recovery
ควร Cache สิ่งที่อ่านบ่อยและเปลี่ยนแบบควบคุมได้
Database ยังเป็น Persistent Source
㊾ LIKE '%word%' ทำไมช้า
MariaDB ระบุว่า Query แบบ
WHERE name LIKE '%police%'
มักใช้ Standard BTREE Index ได้ไม่ดี เพราะ Wildcard อยู่ด้านหน้า
จึงสามารถนำไปสู่ Full Table Scan
ถ้าระบบ Search ใหญ่มากควรพิจารณา
Full-text index
Search architecture
Prefix search
ตาม Requirement
㊿ ORDER BY ช้าแก้อย่างไร
ตัวอย่าง
SELECT *
FROM transactions
WHERE character_id = ?
ORDER BY created_at DESC
LIMIT 50;
หากใช้บ่อย อาจต้องพิจารณา Composite Index
เช่น Concept
(character_id, created_at)
แต่ต้องดู Execution Plan
อย่าเพิ่ม Index ตามตัวอย่างโดยไม่วัด Query ของจริง
51 GROUP BY ช้าได้ไหม
ได้
โดยเฉพาะ Dataset ใหญ่และไม่มี Index ที่ช่วย
EXPLAIN อาจแสดง
Using temporary
Using filesort
ในบาง Query Plans
ไม่ได้หมายความว่า Error แต่เป็นสัญญาณให้ตรวจ Cost และ Index
52 JOIN ช้าแก้อย่างไร
ตรวจว่า Columns ที่ใช้ Join มี Index เหมาะสม
เช่น
SELECT ...
FROM vehicles v
JOIN characters c
ON c.id = v.character_id;
โดยทั่วไป
characters.id
→ Primary Key
และ
vehicles.character_id
ควรได้รับการพิจารณาตาม Query Patterns
53 Rows Examined สำคัญอย่างไร
Query ที่คืน 10 Rows
แต่ต้องตรวจ
500,000 Rows
เป็นสัญญาณว่ามีโอกาส Optimization สูง
MariaDB Slow Query Log สามารถบันทึกข้อมูลอย่าง
query_time
lock_time
rows_sent
rows_examined
ซึ่งมีประโยชน์มากในการหา Query ที่แพงจริง
54 MariaDB Slow Query Log ใช้ได้ไหม
ได้
MariaDB มี Slow Query Log ของตัวเอง นอกเหนือจาก Warning ของ oxmysql
ใช้เพื่อเก็บ Queries ที่ใช้เวลานานตาม long_query_time
และสามารถตั้งให้ Log Query ที่ไม่ใช้ Index ได้ตาม Configuration
เหมาะกับ Production Investigation ที่ต้องการภาพระดับ Database Server
55 Slow Query Log มีข้อมูล Sensitive ไหม
มีได้
MariaDB เตือนว่า SQL Logs อาจมีข้อมูลที่ละเอียดอ่อน เช่น Parameters หรือ Query Text
ดังนั้น Slow Query/General Query Logs ต้องถูกป้องกันเหมือนข้อมูล Server สำคัญ
อย่า Upload Log ทั้งไฟล์สู่ Public เพื่อขอความช่วยเหลือโดยไม่ลบข้อมูลสำคัญ
56 Prepared Query ช่วย Slow Query ไหม
บางกรณี
oxmysql มี MySQL.prepare
สำหรับ Query ที่ใช้บ่อย
แต่ถ้า Query ช้าเพราะ
ไม่มี Index
Scan 5 ล้าน Rows
JOIN ผิด
เปลี่ยนจาก query เป็น prepare อย่างเดียวไม่ได้แก้ Root Cause
57 Transaction ช่วย Performance ไหม
ใช้ Transaction เพื่อ Consistency เป็นหลัก
หลาย Writes ที่สัมพันธ์กันอาจได้ประโยชน์จากการจัด Transaction อย่างเหมาะสม
แต่ Transaction ที่ใหญ่และเปิดนานอาจเพิ่ม Lock Contention
ดังนั้น
Transaction
→ สั้น
→ เฉพาะ Operations ที่ต้อง Atomic
เป็นหลักที่ดี
58 วิธี Debug Slow Query แบบเร็วที่สุด
ใช้ Flow
① หา Query ที่ช้า
② Copy Query ไป Test Environment
③ ใส่ Parameters จริง
④ EXPLAIN
⑤ ดู Rows / Keys
⑥ ตรวจ Index
⑦ ลด Columns
⑧ ลด Rows
⑨ ลด Query Frequency
⑩ ทดสอบใหม่
อย่า Optimize จากความรู้สึก
วัดก่อนและหลังเสมอ
59 Checklist FiveM SQL Error และ Slow Query
ตรวจให้ครบ
① Error Code อะไร?
② Resource ไหนยิง Query?
③ SQL ตัวไหน?
④ Database Name ถูกไหม?
⑤ Table มีไหม?
⑥ Column มีไหม?
⑦ Schema Version ตรงไหม?
⑧ Migration Import แล้วไหม?
⑨ SQL Syntax ถูกไหม?
⑩ Reserved Keyword หรือไม่?
⑪ SQL_MODE มีผลไหม?
⑫ Duplicate Key จริงหรือ Bug?
⑬ NULL ถูกต้องไหม?
⑭ Default Value มีไหม?
⑮ Data Type ตรงไหม?
⑯ Data Length พอไหม?
⑰ Foreign Key ถูกไหม?
⑱ Transaction ค้างหรือไม่?
⑲ Lock wait timeout หรือไม่?
⑳ Deadlock หรือไม่?
㉑ Slow Query Warning เกิดบ่อยไหม?
㉒ mysql_debug จำเป็นไหม?
㉓ EXPLAIN แล้วหรือยัง?
㉔ Query ใช้ Index ไหม?
㉕ Rows examined เท่าไร?
㉖ SELECT * จำเป็นไหม?
㉗ LIMIT เหมาะไหม?
㉘ Query อยู่ใน Loop หรือไม่?
㉙ มี N+1 Query หรือไม่?
㉚ Cache เหมาะไหม?
㉛ LIKE มี Leading Wildcard หรือไม่?
㉜ ORDER BY/GROUP BY หนักหรือไม่?
㉝ JOIN Columns มี Index ไหม?
㉞ Index เยอะเกินไปหรือไม่?
㉟ วัดผลหลังแก้แล้วหรือยัง?
⑥⓪ ตัวอย่างแก้ Slow Query ระบบ Garage
สมมติ Resource มี Query
SELECT *
FROM vehicles
WHERE character_id = ?;
Table มี
2,000,000 vehicles
และไม่มี Index บน
character_id
oxmysql เริ่มแจ้ง Slow Query
ขั้นที่ 1 — ดู Query
แทนที่จะเดา ให้รัน
EXPLAIN
SELECT *
FROM vehicles
WHERE character_id = 100;
ถ้าพบว่า Database ตรวจ Rows จำนวนมากและไม่มี Key ที่เหมาะ
ขั้นที่ 2 — ตรวจ Index
SHOW INDEX
FROM vehicles;
ถ้าไม่มี Index ที่ตรง Query Pattern
อาจพิจารณา
CREATE INDEX idx_vehicles_character_id
ON vehicles(character_id);
ขั้นที่ 3 — ลด Columns
ถ้า Garage List ใช้แค่
id
plate
model
stored
ปรับเป็น
SELECT
id,
plate,
model,
stored
FROM vehicles
WHERE character_id = ?;
ไม่ต้องโหลด JSON Vehicle Properties ขนาดใหญ่ทุกครั้ง
ขั้นที่ 4 — ทดสอบ EXPLAIN อีกครั้ง
EXPLAIN
SELECT
id,
plate,
model,
stored
FROM vehicles
WHERE character_id = 100;
ตรวจว่า Key ถูกใช้และ Rows Estimate ลดลงหรือไม่
ขั้นที่ 5 — วัด oxmysql Warning
Restart/Test Resource ตามความเหมาะสม แล้วดูว่า Query Time ลดลงจริงหรือไม่
นี่คือ Optimization จาก Evidence
ไม่ใช่เดา
ตัวอย่างแก้ N+1 Query
แบบเดิม
local players =
MySQL.query.await(
'SELECT id FROM characters WHERE online = 1'
)
for i = 1, #players do
players[i].vehicles =
MySQL.query.await(
'SELECT id, plate FROM vehicles WHERE character_id = ?',
{
players[i].id
}
)
end
ถ้ามี 100 Players
1 Query
+
100 Queries
อาจกลายเป็น 101 Queries
สามารถออกแบบใหม่ให้โหลด Vehicle Sets เป็น Batch แล้ว Group ฝั่ง Lua หรือใช้ JOIN ตาม Data Shape ที่ต้องการ
สิ่งสำคัญคือเลี่ยง Query จำนวนมากที่เกิดจาก Pattern โดยไม่จำเป็น
ตัวอย่าง Transaction ที่ควรใช้
ซื้อรถ
ต้องทำ
เงินผู้เล่นลด
+
Vehicle Record ถูกสร้าง
ถ้าลดเงินสำเร็จแต่ Insert รถ Fail
ข้อมูลเสีย
จึงควรใช้ Transaction ตาม Architecture
local success =
MySQL.transaction.await({
{
[[
UPDATE characters
SET bank = bank - ?
WHERE id = ?
AND bank >= ?
]],
{
price,
characterId,
price
}
},
{
[[
INSERT INTO vehicles (
character_id,
plate,
model
)
VALUES (?, ?, ?)
]],
{
characterId,
plate,
model
}
}
})
แต่ระบบ Production ต้องตรวจ Affected Rows และ Business State เพิ่ม ไม่ใช่ถือว่า Transaction อย่างเดียวทำให้ Economy ปลอดภัยทั้งหมด
ตัวอย่าง Duplicate Entry ที่ควรแก้ใน Logic
สมมติ Plate เป็น Unique
UNIQUE KEY unique_plate (plate)
ก่อน Insert Vehicle ควรสร้าง Plate ที่ Server ควบคุมและมีวิธีรับมือ Collision
ไม่ควรแก้ Error ด้วย
DROP UNIQUE INDEX
ถ้ากฎของระบบคือรถทุกคันต้องมี Plate ไม่ซ้ำ
Database Constraint กำลังช่วยป้องกันข้อมูลเสีย
ตัวอย่าง Deadlock ในระบบโอนเงิน
สมมติ Transaction A
Lock Account 1
↓
Lock Account 2
แต่ Transaction B
Lock Account 2
↓
Lock Account 1
มีโอกาส Deadlock
การกำหนด Lock/Update Order ให้สม่ำเสมอ เช่นเรียง Account ID ก่อนเสมอ สามารถลด Deadlock Pattern บางประเภทได้
และ Operation ที่เหมาะควรสามารถ Retry เมื่อพบ Deadlock ตามการออกแบบ
Slow Query ไม่ได้แก้ด้วย Index อย่างเดียว
บาง Query ช้าเพราะ
เรียกบ่อยเกินไป
เช่น Query 5 ms
ดูเร็วมาก
แต่เรียก
5,000 ครั้ง/วินาที
ก็เป็น Load ใหญ่
Performance ต้องดู
Cost ต่อ Query
×
Frequency
×
Concurrent Players
เสมอ
Query 300 ms ครั้งเดียวกับ 10 ms 1,000 ครั้ง อะไรแย่กว่า
ต้องดู Workload จริง
Query 300 ms ที่รันตอน Server Startup วันละครั้งอาจแทบไม่มีผลกับ Player
แต่ Query 10 ms ที่รันทุก Player ทุกวินาทีอาจสร้าง Load มากกว่า
จึงเป็นเหตุผลที่ oxmysql Documentation ระบุว่า Slow Query Warning โดยเฉพาะช่วง Startup ไม่จำเป็นต้องเป็นปัญหาเสมอไป
คำถามที่พบบ่อยเกี่ยวกับ FiveM SQL Error และ Slow Query
FiveM Unknown column แก้อย่างไร
ตรวจ Table Schema และ Migration ว่า Column ที่ Query ใช้มีจริงหรือไม่
Table doesn't exist แก้อย่างไร
Import/Create Schema ที่ Resource ต้องการและตรวจว่ากำลังเชื่อม Database ถูกตัว
Duplicate entry แก้อย่างไร
ตรวจ Unique/Primary Key และแก้ Logic ที่พยายาม Insert Duplicate หากข้อมูลควร Unique จริง
SQL Syntax Error แก้อย่างไร
อ่านตำแหน่ง Error ตรวจ Syntax, Reserved Keyword และ SQL_MODE/Database Version
Column cannot be null คืออะไร
Script ส่ง NULL เข้า Column ที่กำหนด NOT NULL
Data too long คืออะไร
ข้อมูลยาวเกิน Capacity ของ Column
Lock wait timeout คืออะไร
Transaction รอ Lock เกิน Timeout
Deadlock คืออะไร
Transactions หลายตัวถือ Lock และรอกันจน Database ต้องยกเลิกบาง Transaction
Slow Query ใน oxmysql คืออะไร
Query ที่ใช้เวลานานเกิน mysql_slow_query_warning
Default Threshold oxmysql เท่าไร
Source ปัจจุบันกำหนด Default ที่ประมาณ 200 milliseconds หากไม่ได้ Override
ตั้ง Slow Query Warning เองได้ไหม
ได้ เช่น
set mysql_slow_query_warning 150
Slow Query Warning แปลว่า Database เสียไหม
ไม่ Server Hitch หรือ Startup Load ก็ทำให้ Timing สูงได้
EXPLAIN คืออะไร
ใช้ดู Execution Plan ว่า Database จะอ่าน Query อย่างไรและใช้ Index ใด
Index ช่วยอะไร
ช่วยลดการค้นหา Rows สำหรับ Query Pattern ที่เหมาะสม
ควร Index ทุก Column ไหม
ไม่ Extra Index เพิ่ม Storage และทำให้ Writes แพงขึ้น
SELECT * ไม่ดีเสมอไปไหม
ไม่ แต่ควรเลือกเฉพาะ Columns ที่ต้องใช้เมื่อ Table มีข้อมูลมากหรือ Columns ใหญ่
Query ทุก Tick ได้ไหม
ไม่ควร
N+1 Query คืออะไร
Pattern ที่โหลดรายการหนึ่งครั้งแล้ว Query แยกเพิ่มทีละ Record จนเกิด Query จำนวนมาก
LIKE '%keyword%' ช้าทำไม
Leading Wildcard มักทำให้ Standard BTREE Index ใช้ได้ไม่เต็มประสิทธิภาพและอาจเกิด Full Scan
Prepared Query แก้ Slow Query ได้ทุกกรณีไหม
ไม่ ถ้า Root Cause คือ Index หรือ Query Design ผิดก็ยังต้องแก้ตรงนั้น
Transaction ช่วยอะไร
ช่วยให้หลาย Database Operations สำเร็จหรือล้มเหลวเป็นชุดตาม Transaction Logic
Transaction ยิ่งใหญ่ยิ่งดีไหม
ไม่ Transaction ยาวสามารถถือ Locks นานและเพิ่ม Contention
MariaDB Slow Query Log ใช้ได้ไหม
ได้ และสามารถเก็บ Query Time, Lock Time, Rows Sent และ Rows Examined เพื่อวิเคราะห์ได้
mysql_debug เปิด Production ได้ไหม
เปิดได้แต่ควรใช้เท่าที่จำเป็น เนื่องจาก Query Logging จำนวนมากสร้าง Noise และอาจมีข้อมูลสำคัญ
สรุป FiveM SQL Error และ Slow Query แก้อย่างไร
วิธีแก้ที่ถูกต้องคือเริ่มจากแยกก่อนว่า
Query พัง
หรือ
Query ช้า
ถ้า Query พัง ให้ตรวจ
Table
Column
Schema
Syntax
Data Type
Unique Constraint
NULL
Transaction
ถ้า Query ทำงานแต่ช้า ให้ใช้
Slow Query Warning
↓
EXPLAIN
↓
Index
↓
Rows Examined
↓
Query Frequency
↓
Cache / Batch
↓
Measure Again
ตัวอย่างการตั้งค่า oxmysql
set mysql_slow_query_warning 150
และเมื่อหา Query ช้าเจอ
EXPLAIN
SELECT
id,
plate,
model
FROM vehicles
WHERE character_id = ?;
จากนั้นจึงพิจารณา Index ที่ตรง Query Pattern
CREATE INDEX idx_vehicles_character_id
ON vehicles(character_id);
แต่ห้ามใช้แนวคิด
Query ช้า
↓
สร้าง Index ทุก Column
เพราะ Index ที่ไม่จำเป็นทำให้
INSERT
UPDATE
DELETE
แพงขึ้นและใช้ Storage เพิ่ม
สำหรับ Developer ที่เรียน FiveM กับ comsiam สิ่งสำคัญคือ อย่า Optimize จากความรู้สึก ให้หา Query ที่ช้าจริง ดู Execution Plan แล้ววัดผลก่อนและหลังแก้
หลักสำคัญจาก comsiam อีกข้อคือ Database Performance ไม่ได้ขึ้นกับ SQL หนึ่งบรรทัดอย่างเดียว แต่เป็น
Query Cost
×
จำนวนครั้งที่ Query
×
จำนวนผู้เล่น
×
จำนวน Rows
Query ที่เร็ว 5 ms อาจกลายเป็นปัญหาใหญ่ถ้ายิงหลายพันครั้งต่อวินาที ในขณะที่ Query 300 ms ที่รันเพียงครั้งตอน Startup อาจแทบไม่มีผลต่อ Gameplay
หัวข้อถัดไปคือ FiveM NUI คืออะไร ใช้ HTML CSS JavaScript อย่างไร ซึ่งจะเปลี่ยนจาก Database มาสู่ระบบ UI และอธิบายความสัมพันธ์ระหว่าง Lua/JavaScript ฝั่ง Client กับ Browser NUI, ui_page, files, SendNUIMessage, NUI Callback และ Focus แบบครบวงจร
Comments
Post a Comment