FiveM MySQL Transaction คืออะไร? วิธีใช้ oxmysql Transaction ให้หลาย Query สำเร็จหรือย้อนกลับพร้อมกัน
FiveM MySQL Transaction คือวิธีรวม SQL หลายคำสั่งให้ทำงานเป็นชุดเดียว โดย oxmysql ระบุว่า Transaction จะ Commit ก็ต่อเมื่อทุก Query สำเร็จ และหาก Query ใด Query หนึ่งล้มเหลว Queries ใน Transaction นั้นจะไม่ถูก Commit.
แนวคิดนี้สำคัญมากกับ FiveM Server ที่ Action หนึ่งต้องแก้ข้อมูลหลาย Tables เพราะถ้าใช้ Queries แยกกันแล้ว Query กลางทางล้มเหลว อาจเกิดข้อมูล “สำเร็จครึ่งเดียว” เช่น Character ถูกสร้างแล้วแต่ Settings ไม่ถูกสร้าง หรือข้อมูลใน Table หนึ่งเปลี่ยนแต่ Table ที่เกี่ยวข้องไม่เปลี่ยนตาม
จำหลักง่ายๆ:
หลาย Query
+
ต้องสำเร็จเป็นชุดเดียว
=
Transaction
① FiveM Transaction คืออะไร
สมมติ Action หนึ่งต้องทำ:
Query A
↓
Query B
↓
Query C
หากใช้ Transaction:
A สำเร็จ
B สำเร็จ
C สำเร็จ
↓
COMMIT
แต่ถ้า:
A สำเร็จ
B ล้มเหลว
C ไม่สำเร็จ
↓
ไม่ Commit ชุด Transaction
นี่คือ Behavior หลักที่ oxmysql ระบุไว้สำหรับ MySQL.transaction.
② Commit คืออะไร
Commit หมายถึงการยืนยันการเปลี่ยนแปลงของ Transaction ลง Database
Flow:
BEGIN
↓
Query 1
↓
Query 2
↓
Query 3
↓
ทุกอย่างสำเร็จ
↓
COMMIT
ใน oxmysql Developer ไม่จำเป็นต้องเขียน BEGIN และ COMMIT เองเมื่อใช้ MySQL.transaction() เพราะ Resource จัดการ Transaction Flow ให้ตาม API.
③ Rollback คืออะไร
แนวคิด Rollback คือไม่ยืนยันการเปลี่ยนแปลงของ Transaction เมื่อชุด Queries ไม่สามารถสำเร็จครบ
ในระดับ API oxmysql อธิบายว่า หากหนึ่ง Query ล้มเหลว ไม่มี Query ใน Transaction นั้นถูก Commit.
จึงช่วยป้องกันสถานะ:
Table A เปลี่ยน
แต่
Table B ไม่เปลี่ยน
เมื่อสองการเปลี่ยนแปลงควรเกิดพร้อมกัน
④ MySQL.transaction คืนค่าอะไร
oxmysql ระบุว่า Return Value เป็น:
boolean
ดังนั้น:
local success =
MySQL.transaction.await(queries)
if success then
print('Transaction successful')
else
print('Transaction failed')
end
สามารถใช้ตรวจว่า Transaction สำเร็จหรือไม่.
⑤ Syntax พื้นฐานของ MySQL.transaction.await
ตัวอย่าง:
local queries = {
{
query = [[
INSERT INTO `profiles`
(`identifier`, `display_name`)
VALUES (?, ?)
]],
values = {
identifier,
displayName
}
},
{
query = [[
INSERT INTO `profile_settings`
(`identifier`, `theme`)
VALUES (?, ?)
]],
values = {
identifier,
'default'
}
}
}
local success =
MySQL.transaction.await(queries)
print(success)
oxmysql รองรับ Specific Format ซึ่งแต่ละ Query มี Parameters ของตัวเองแบบนี้.
⑥ Specific Format คืออะไร
รูปแบบหนึ่งที่ oxmysql รองรับคือ:
local queries = {
{
query = 'INSERT INTO `table_a` (`id`) VALUES (?)',
values = { 1 }
},
{
query = 'INSERT INTO `table_b` (`id`, `name`) VALUES (?, ?)',
values = {
2,
'Example'
}
}
}
แต่ละ Query จึงมี:
SQL ของตัวเอง
+
Parameters ของตัวเอง
Official Documentation เรียกรูปแบบนี้ว่า Specific Format.
⑦ Array Format ใช้ได้ไหม
ได้
oxmysql ยังรองรับ:
local queries = {
{
'INSERT INTO `table_a` (`id`) VALUES (?)',
{ 1 }
},
{
'INSERT INTO `table_b` (`id`, `name`) VALUES (?, ?)',
{
2,
'Example'
}
}
}
local success =
MySQL.transaction.await(queries)
เป็นอีก Syntax ที่ Documentation รองรับ.
⑧ Shared Parameters คืออะไร
oxmysql ยังมี Shared Format ที่สามารถส่งชุด Queries พร้อม Parameters ร่วมกันได้ โดย Documentation รองรับ Transaction Formats หลายแบบ.
แต่สำหรับ Resource ใหม่ที่แต่ละ Query มี Parameters ต่างกัน การใช้ Specific Format มักอ่านง่ายกว่า เช่น:
Query
+
Values
Query
+
Values
เห็นความสัมพันธ์ของ Parameters กับ SQL ชัดเจน
⑨ Transaction เหมาะใช้เมื่อไหร่
ใช้เมื่อหลาย Operations มีความสัมพันธ์กันและไม่ควรเกิดเพียงบางส่วน
ตัวอย่าง:
สร้าง Character
+
สร้าง Character Settings
หรือ:
สร้าง Persistent Record
+
สร้าง Related Metadata
ถ้า Query แรกสำเร็จแต่ Query หลังล้มเหลว ข้อมูลอาจไม่ครบ
Transaction ช่วยป้องกันปัญหานี้ด้วยการ Commit เมื่อทุก Query สำเร็จเท่านั้น.
⑩ Transaction ไม่จำเป็นกับ Query เดียว
ถ้ามีเพียง:
UPDATE `profiles`
SET `display_name` = ?
WHERE `id` = ?;
Query เดียวโดยทั่วไปไม่จำเป็นต้องห่อ MySQL.transaction() เพียงเพื่อเรียก Update หนึ่งครั้ง
ใช้:
local affected =
MySQL.update.await(
[[
UPDATE `profiles`
SET `display_name` = ?
WHERE `id` = ?
]],
{
displayName,
profileId
}
)
MySQL.update คืนจำนวน Rows ที่ได้รับผล.
⑪ Transaction ไม่ได้หมายถึง Batch ทุกอย่าง
อย่าคิดว่า:
มี 100 Query
↓
ต้อง Transaction ทั้งหมด
ถามก่อนว่า Queries เหล่านั้นต้องมี Atomic Relationship กันหรือไม่
ถ้าทำงานแยกอิสระกัน การบังคับให้อยู่ Transaction ใหญ่เดียวอาจไม่จำเป็น
⑫ Atomic หมายถึงอะไร
ในบริบทนี้ให้จำง่ายๆ ว่า:
ทั้งหมด
หรือ
ไม่มีเลย
ถ้า Action ต้องมี:
Record A
+
Record B
+
Record C
พร้อมกัน
Transaction เหมาะกับ Requirement นี้
⑬ ตัวอย่างสร้าง Character แบบ Transaction
สมมติ Schema มี:
characters
character_settings
สามารถเขียน:
local queries = {
{
query = [[
INSERT INTO `characters`
(`identifier`, `firstname`, `lastname`)
VALUES (?, ?, ?)
]],
values = {
identifier,
firstName,
lastName
}
},
{
query = [[
INSERT INTO `character_settings`
(`identifier`, `theme`)
VALUES (?, ?)
]],
values = {
identifier,
'default'
}
}
}
local success =
MySQL.transaction.await(queries)
if not success then
print('Character transaction failed')
return
end
Transaction จะคืน Boolean บอกผลของชุด Queries.
⑭ ข้อจำกัดของตัวอย่าง Character ด้านบน
ตัวอย่างนั้นใช้ identifier เชื่อม Records เพื่อให้ Code อ่านง่าย
ใน Production Schema จริงอาจใช้:
character_id
Foreign Key
Internal UUID
แทน
ต้องออกแบบตาม Framework และ Database Schema ของ Server จริง
⑮ ถ้าต้องใช้ Insert ID จาก Query แรกทำอย่างไร
นี่เป็นเรื่องที่ต้องวาง Flow ให้ดี
MySQL.insert ปกติสามารถคืน Insert ID ของ Row ที่สร้าง.
แต่ Transaction API คืนค่า Boolean ของ Transaction ไม่ได้คืน Insert ID ของแต่ละ Queryโดยตรงในรูปแบบเดียวกับ MySQL.insert.
ดังนั้นหาก Queries ภายใน Transaction ต้องสัมพันธ์กับ ID ที่สร้างใหม่ ต้องออกแบบ SQL/Schema ให้รองรับ Dependency นั้นอย่างชัดเจน
⑯ LAST_INSERT_ID ใช้ได้ไหม
Official oxmysql Benchmark มีตัวอย่าง Transaction ที่:
INSERT INTO `test_table` ...
ตามด้วย:
UPDATE `test_table`
SET `username` = ?
WHERE `id` = LAST_INSERT_ID()
ภายใน Transaction เดียวกัน.
นี่แสดงว่า Workflow ที่อาศัย LAST_INSERT_ID() สามารถถูกใช้ภายใน Transaction ตาม Connection Context ของตัวอย่าง oxmysql ได้
⑰ ตัวอย่าง LAST_INSERT_ID
local queries = {
{
query = [[
INSERT INTO `profiles`
(`identifier`)
VALUES (?)
]],
values = {
identifier
}
},
{
query = [[
UPDATE `profiles`
SET `display_name` = ?
WHERE `id` = LAST_INSERT_ID()
]],
values = {
displayName
}
}
}
local success =
MySQL.transaction.await(queries)
แนวทางนี้อิง Pattern ที่ oxmysql Benchmark ใช้ใน Transaction Example.
⑱ Transaction กับ MySQL.insert ต่างกันอย่างไร
MySQL.insert
เหมาะเมื่อ:
เพิ่มหนึ่ง Record
+
ต้องการ Insert ID
Official API คืน Insert ID หากถูกต้อง.
MySQL.transaction
เหมาะเมื่อ:
หลาย Query
+
ต้องสำเร็จครบ
และ Return Value เป็น Boolean.
⑲ Transaction กับ MySQL.update ต่างกันอย่างไร
MySQL.update:
ทำ Query Update
↓
คืน affectedRows
Transaction:
หลาย Query
↓
คืน success true/false
เลือก API ให้ตรงกับสิ่งที่ต้องการรู้จาก Result
⑳ Transaction กับ Prepare ต่างกันอย่างไร
Transaction
เน้น:
Data Consistency
Prepare
oxmysql ระบุว่าเหมาะกับ Query ที่ถูกเรียกบ่อยและสามารถ Execute Frequently-called Queries ได้เร็วขึ้น.
ดังนั้น:
Transaction
≠
Prepare
หนึ่งตัวแก้เรื่อง Atomicity อีกตัวเกี่ยวกับ Execution ของ Query ที่เรียกซ้ำ
㉑ Transaction ทำให้ Query เร็วขึ้นไหม
ไม่ควรใช้ด้วยเหตุผลนี้เป็นหลัก
หน้าที่ของ Transaction คือให้หลาย Queries Commit เมื่อทั้งหมดสำเร็จ.
Performance ต้องวัดแยก เพราะ oxmysql ระบุว่า Query Speeds แตกต่างตาม:
Hardware
Database Settings
Database Version
Current Workload
㉒ Transaction ยิ่งใหญ่ยิ่งดีไหม
ไม่
Transaction ควรมี Scope เท่าที่จำเป็น
ถ้า Action ต้องแก้เพียง:
Table A
+
Table B
ก็ไม่จำเป็นต้องลาก Queries ที่ไม่เกี่ยวข้องอีก 30 ตัวเข้ามาเพียงเพราะเกิดในช่วงเวลาเดียวกัน
Transaction ที่อ่านง่ายควรสะท้อน Business Operation หนึ่งชุด
㉓ Transaction Isolation Level คืออะไร
oxmysql รองรับ ConVar:
mysql_transaction_isolation_level
โดยรับค่าตัวเลข:
1–4
และ Documentation ปัจจุบันระบุ Default เป็น:
2
㉔ Isolation Level 1 คืออะไร
oxmysql Mapping ปัจจุบันระบุ:
1
=
Repeatable Read
㉕ Isolation Level 2 คืออะไร
2
=
Read Committed
และเป็น Default ที่ oxmysql Documentation ปัจจุบันระบุ.
㉖ Isolation Level 3 คืออะไร
3
=
Read Uncommitted
ตาม Mapping ของ oxmysql.
㉗ Isolation Level 4 คืออะไร
4
=
Serializable
ตาม Transaction Documentation ปัจจุบัน.
㉘ ตาราง Isolation Level ของ oxmysql
| ค่า | Isolation Level |
|---|---|
1 | Repeatable Read |
2 | Read Committed |
3 | Read Uncommitted |
4 | Serializable |
Default ปัจจุบันคือ 2 หรือ Read Committed.
㉙ ควรเปลี่ยน Isolation Level ไหม
ถ้ายังไม่มีเหตุผลด้าน Database Concurrency ที่ชัดเจน ไม่ควรเปลี่ยนเพียงเพราะเห็นว่าระดับเลขสูงกว่า
Isolation Level มี Trade-off และเกี่ยวข้องกับ Behavior ของ Concurrent Transactions
ถ้า Server ปัจจุบันทำงานปกติ ควรเริ่มจาก Default ของ oxmysql และเปลี่ยนเฉพาะเมื่อเข้าใจผลต่อ Workload จริง.
㉚ วิธีตั้ง Transaction Isolation Level
ตัวอย่าง:
set mysql_transaction_isolation_level 2
ค่าปัจจุบัน 2 หมายถึง Read Committed ตาม oxmysql Mapping.
อย่า Copy ค่าอื่นจาก Server คนอื่นโดยไม่เข้าใจเหตุผล
㉛ Transaction ช่วยเรื่อง Concurrent Requests ไหม
Transaction ช่วยรักษาความเป็นชุดของ Database Operations แต่ไม่ได้หมายความว่า Application Logic จะปลอดจาก Race Condition ทุกประเภทโดยอัตโนมัติ
FiveM Server ยังต้องตรวจ:
Current State
Permission
Record Ownership
Request Validity
ก่อนเริ่ม Database Operation
Cfx.re แนะนำให้ Client-triggered Network Events ถูกตรวจด้วยข้อมูลฝั่ง Server ไม่ให้ Client เป็นผู้กำหนดค่าที่สำคัญเอง.
㉜ Client ขอ Database Action ควรทำอย่างไร
Architecture:
Client
↓
Request Action
↓
Server Validate
↓
Load/Check Server State
↓
Transaction
↓
Success / Failure
↓
Server ส่งผลกลับ
ไม่ควร:
Client ส่งข้อมูล
↓
Database UPDATE ทันที
โดยไม่มี Validation
㉝ ตัวอย่าง Server Event ที่ควรหลีกเลี่ยง
RegisterNetEvent(
'profile:update',
function(profileId, newOwner)
MySQL.update.await(
[[
UPDATE `profiles`
SET `owner` = ?
WHERE `id` = ?
]],
{
newOwner,
profileId
}
)
end
)
ปัญหาคือ Client เป็นผู้ส่งทั้ง:
profileId
newOwner
และ Server ไม่ตรวจสิทธิ์ก่อน
Cfx.re เตือนว่าข้อมูลจาก Network Event ควรถูกตรวจด้วย Server-side Methods.
㉞ ตัวอย่าง Server Validation ที่ดีกว่า
RegisterNetEvent(
'profile:updateName',
function(profileId, displayName)
local src = source
if type(profileId) ~= 'number' then
return
end
if type(displayName) ~= 'string' then
return
end
if #displayName < 1
or #displayName > 40 then
return
end
local identifier =
GetPlayerIdentifierByType(
src,
'license'
)
if not identifier then
return
end
local affected =
MySQL.update.await(
[[
UPDATE `profiles`
SET `display_name` = ?
WHERE `id` = ?
AND `identifier` = ?
]],
{
displayName,
profileId,
identifier
}
)
if affected == 0 then
return
end
end
)
ตัวอย่างนี้ตรวจ Input และยืนยัน Ownership ผ่านข้อมูล Server/Database มากขึ้น ซึ่งสอดคล้องกับ Cfx.re Server Event Security Guidance.
㉟ Transaction ยังต้อง Validate Client ไหม
ต้อง
Transaction รับประกันเรื่อง Database Operations เป็นชุด
ไม่ได้รับประกันว่า:
Player มีสิทธิ์
Request ถูกต้อง
ข้อมูล Client จริง
ดังนั้น:
Security Validation
≠
Transaction
เป็นคนละ Layer
㊱ Transaction กับ SQL Injection
Transaction ไม่ได้แทน Parameterized Query
ยังควรเขียน:
{
query = [[
UPDATE `profiles`
SET `display_name` = ?
WHERE `id` = ?
]],
values = {
displayName,
profileId
}
}
แทนการเอา User Input ต่อเป็น SQL String โดยตรง
㊲ อย่าต่อ Client Input เข้า Query String
หลีกเลี่ยง:
local sql =
"UPDATE profiles SET display_name = '" ..
displayName ..
"' WHERE id = " ..
profileId
ใช้ Placeholders:
UPDATE `profiles`
SET `display_name` = ?
WHERE `id` = ?
และส่ง Parameters แยกจาก Query Structure
㊳ Transaction Failed ต้องทำอะไร
เมื่อ:
local success =
MySQL.transaction.await(queries)
if not success then
-- handle failure
end
ควรมี Failure Handling เช่น:
Log Error
ไม่เปลี่ยน Runtime State เป็น Success
ไม่ส่ง Success ให้ Client
Retry เฉพาะเมื่อ Architecture รองรับ
อย่าทำเหมือน Transaction สำเร็จหาก Return Value เป็น false.
㊴ อย่า Update Runtime State ก่อน Transaction สำเร็จ
ตัวอย่างที่ควรระวัง:
เปลี่ยน Cache ก่อน
↓
Transaction Database
↓
Transaction ล้มเหลว
↓
Cache กับ Database ไม่ตรง
Pattern ที่ปลอดภัยกว่าในหลายระบบ:
Validate
↓
Transaction
↓
Success
↓
Update Runtime State
หรือมี Rollback Strategy สำหรับ Runtime State ให้ชัดเจน
㊵ Database สำเร็จแต่ส่ง Event กลับ Client ไม่ได้ทำอย่างไร
ต้องแยก:
Database Transaction
ออกจาก:
Network Delivery
Transaction สามารถ Commit สำเร็จ แต่ Client อาจ Disconnect หลังจากนั้น
ดังนั้น Persistent State ควรยึด Database/Server State เป็นหลัก ไม่ควรถือว่า Client Acknowledgement เป็นเงื่อนไขของ Database Commit เสมอไป
㊶ Player Disconnect ระหว่าง Transaction
Server-side Database Operation อาจดำเนินต่อใน Context ของ Resourceแม้ Player Connection State เปลี่ยน
ดังนั้นหลัง Await หากต้องส่งข้อมูลกลับ Client ควรพิจารณาว่า Source ยัง Valid สำหรับ Action ต่อหรือไม่
แต่ Database Consistency ไม่ควรผูกกับการที่ Client ต้องออนไลน์ตลอดช่วง Operation
㊷ Transaction กับ Player Save
ถ้าการ Save Player หนึ่งครั้งต้อง Update:
profile
settings
statistics
และทั้งสามต้องสอดคล้องกัน Transaction อาจเหมาะ
แต่ถ้าแต่ละ Table เป็น Independent Data ที่ Save แยกได้ ก็ไม่จำเป็นต้องบังคับ Transaction เดียวเสมอ
ออกแบบตาม Consistency Requirement
㊸ Transaction กับ Character Creation
Character Creation เป็น Use Case ที่เข้าใจง่าย:
Character
↓
Default Settings
↓
Profile Metadata
ถ้าต้องมีทั้งหมดก่อนถือว่า Character พร้อมใช้ Transaction ช่วยไม่ให้เหลือ Character Record ที่สร้างไม่ครบ
㊹ Transaction กับ Delete Data
ถ้า Resource ต้องลบ Related Records หลาย Table:
profile_settings
↓
profile_statistics
↓
profiles
และต้องลบสำเร็จเป็นชุดเดียว Transaction สามารถเหมาะได้
แต่อีกแนวทางหนึ่งคือใช้ Database Constraints/Foreign Key Cascades ตาม Schema Design
ต้องเลือกให้ตรง Framework และ Database Architecture
㊺ Transaction กับ Schema Migration
ไม่ควรสมมติว่า Database DDL ทุกประเภทจะมี Transaction Behavior เหมือน Data Manipulation Queries
Schema Migration ควรถูกออกแบบและทดสอบแยกจาก Gameplay Transaction
ก่อน Migration Production ต้อง:
Backup
Test
อ่าน Migration Notes
เสมอ
㊻ Transaction กับ Slow Query
Transaction ไม่ได้แก้ Slow Query โดยอัตโนมัติ
หาก Query ภายใน Transaction ช้า:
Query A = 10 ms
Query B = 500 ms
Query C = 20 ms
ต้อง Optimize Query B ตามปกติ เช่น:
EXPLAIN
Index
Query Design
Workload
Transaction เพียงทำให้ Operations เป็นชุด
㊼ Transaction ใหญ่เกินไปอาจมีผลอย่างไร
ยิ่ง Transaction มีงานจำนวนมาก ก็ยิ่งควรระวัง:
เวลาที่ Transaction เปิดอยู่
จำนวน Rows ที่แตะ
Concurrency
Query Time
Failure Probability
จึงควรรวมเฉพาะ Operations ที่ต้อง Atomic จริง
㊽ Query Performance วัดอย่างไร
oxmysql ระบุว่า Real Query Speeds สามารถดูผ่าน Debug UI และ Server Console เมื่อเปิด mysql_debug และความเร็วแตกต่างตาม Hardware, Database Settings, Version และ Workload.
ดังนั้นเมื่อ Transaction ช้า:
mysql_debug
↓
ดู Query ภายใน
↓
หา Query ที่กินเวลา
แทนการสรุปว่า Transaction API ช้าเอง
㊾ Transaction ไม่ควรถูกใช้แทน Error Handling
แม้ Transaction จะป้องกัน Partial Commit แต่ Resource ยังต้อง:
Check success
Log failure
Handle client response
Recover runtime state
ด้วยตัวเอง
ตัวอย่าง:
local success =
MySQL.transaction.await(queries)
if not success then
print(
'[myresource] database transaction failed'
)
return false
end
return true
㊿ Transaction Callback ใช้ได้ไหม
ได้
oxmysql รองรับ Callback Style เช่น:
MySQL.transaction(
queries,
function(success)
print(success)
end
)
นอกจาก Promise/Await Style.
51. Await กับ Callback เลือกแบบไหนดี
ขึ้นกับ Code Style และ Resource Architecture
Await
local success =
MySQL.transaction.await(queries)
อ่าน Flow ต่อเนื่องง่าย
Callback
MySQL.transaction(
queries,
function(success)
-- next step
end
)
ทั้งสองแบบได้รับการรองรับโดย oxmysql.
52. ตัวอย่าง Transaction Function ที่นำกลับใช้ได้
local function createProfile(
identifier,
displayName
)
local queries = {
{
query = [[
INSERT INTO `profiles`
(`identifier`, `display_name`)
VALUES (?, ?)
]],
values = {
identifier,
displayName
}
},
{
query = [[
INSERT INTO `profile_settings`
(`identifier`, `theme`)
VALUES (?, ?)
]],
values = {
identifier,
'default'
}
}
}
local success =
MySQL.transaction.await(queries)
return success == true
end
ฟังก์ชันนี้ทำให้ Call Site อ่านง่าย:
if not createProfile(
identifier,
displayName
) then
return
end
53. ป้องกัน Duplicate Request อย่างไร
Transaction ไม่ได้หยุด Client จาก Trigger Event ซ้ำโดยอัตโนมัติ
Resource ควรมี:
Server-side State
Unique Constraint
Request Lock
หรือ
Application Validation
ตาม Use Case
เช่น Character Creation ควรตรวจ Server-side ว่า Player ยังอยู่ใน Creation State ที่ถูกต้อง และ Database Schema อาจใช้ Unique Constraint ในข้อมูลที่ต้องไม่ซ้ำ
54. Unique Constraint สำคัญกับ Transaction ไหม
ช่วยป้องกัน Duplicate Data ในระดับ Database
หาก Query หนึ่งใน Transaction ละเมิด Constraint และล้มเหลว Transaction ก็จะไม่ Commit ทั้งชุดตาม Behavior ที่ oxmysql ระบุ.
นี่ช่วยให้ Database Schema ทำงานร่วมกับ Application Validation ได้
55. Transaction ไม่แทน Unique Constraint
อย่าใช้ Code อย่างเดียวเพื่อรับประกัน Unique Data
ถ้า Business Rule ระบุ:
ค่าบางอย่างห้ามซ้ำ
Database Constraint ควรถูกพิจารณาด้วย เพราะ Concurrent Requests อาจเกิดใกล้กันมาก
Application Validation และ Database Constraints จึงควรเสริมกัน
56. Race Condition คืออะไรใน FiveM Database
ตัวอย่าง:
Request A
ตรวจว่า Record ยังไม่มี
↓
Request B
ตรวจว่า Record ยังไม่มี
↓
A Insert
↓
B Insert
หากไม่มี Constraint/Concurrency Design อาจได้ Duplicate Records
Transaction อย่างเดียวไม่จำเป็นต้องแก้ Race Pattern นี้ทุกแบบ
ต้องออกแบบ Database Constraints และ Query Flow เพิ่ม
57. Transaction กับ Upsert ใช้แทนกันไหม
ไม่ทั้งหมด
Upsert
เหมาะกับ:
Insert ถ้ายังไม่มี
หรือ
Update ถ้ามี
Transaction
เหมาะกับ:
หลาย Queries
ต้องสำเร็จทั้งหมด
บางระบบสามารถใช้ Upsert ภายใน Transaction ได้ หากตรงกับ Requirement จริง
58. Transaction กับ Prepared Query ใช้ร่วมกันอย่างไร
Prepare มี API แยกสำหรับ Frequently-called Query และ Transaction มี API สำหรับ Multiple Queries.
ไม่ควรพยายามรวมทุก Optimization Technique โดยอัตโนมัติ
วัดก่อนว่า Bottleneck คือ:
Query Frequency
Database Access
หรือ
Consistency
59. Transaction Isolation Level ควรเหมือนทุก Server ไหม
ไม่จำเป็น
oxmysql มี Default 2 หรือ Read Committed แต่เปิดให้ปรับตั้งแต่ 1–4.
Server ที่ต้องการเปลี่ยนควรเข้าใจ Database Concurrency และทดสอบ Workload จริงก่อน
60. Checklist ก่อนใช้ MySQL.transaction
ตรวจให้ครบ:
มีมากกว่าหนึ่ง Query จริงหรือไม่
Queries ต้องสำเร็จพร้อมกันหรือไม่
ถ้า Query หนึ่งล้มเหลว อีก Query ควรถูกยกเลิกหรือไม่
ใช้ Parameters แยกจาก SQL
Validate Client Input แล้วหรือไม่
ตรวจ Player Permission แล้วหรือไม่
ตรวจ Record Ownership แล้วหรือไม่
Query แต่ละตัวมี Index ที่เหมาะสมหรือไม่
Transaction ใหญ่เกินไปหรือไม่
Runtime State เปลี่ยนก่อน Commit หรือไม่
มี Failure Handling หรือไม่
มี Error Logging หรือไม่
Client Disconnect แล้ว Flow ยังถูกหรือไม่
มี Duplicate Request Protection หรือไม่
มี Unique Constraints ที่จำเป็นหรือไม่
Query Count สูงหรือไม่
Transaction ถูกเรียกทุก Tick หรือไม่
Slow Query อยู่ใน Transaction หรือไม่
เปิด mysql_debug เมื่อต้องวิเคราะห์หรือไม่
ทดสอบ Success Path แล้วหรือไม่
ทดสอบ Failure Path แล้วหรือไม่
ทดสอบ Query ที่สองล้มเหลวหรือยัง
ตรวจว่า Query แรกไม่ถูก Commit เมื่อ Query หลังล้มเหลว
ตรวจ Database หลัง Resource Restart
Backup ก่อนเปลี่ยน Schema
61. ตัวอย่างทดสอบ Transaction Failure
ใน Development Database สามารถสร้าง Scenario ที่ Query หลังล้มเหลวโดยตั้งใจ เพื่อยืนยันว่า Query ก่อนหน้าไม่ถูก Commit
แนวคิด:
Transaction
↓
Query 1 Valid
↓
Query 2 Invalid
↓
success = false
↓
ตรวจ Database
↓
Query 1 ต้องไม่ถูก Commit
Behavior นี้ตรงกับ Guarantee ที่ oxmysql ระบุสำหรับ Transaction.
62. อย่าทดสอบ Failure บน Production Data
การทดสอบ Transaction Failure ควรทำใน:
Development Database
หรือ
Staging
ไม่ใช่ใช้ Player Data จริง
โดยเฉพาะ Query ประเภท Delete/Update
63. Transaction กับ Backup ยังต้องมีไหม
ต้องมี
Transaction ป้องกัน Partial Commit ของ Operation หนึ่งชุด
Backup ป้องกันปัญหาอีกระดับ เช่น:
Schema Error
Resource Bug
Admin Error
Database Corruption
Migration ผิด
จึงไม่ใช่ระบบที่แทนกัน
64. Transaction กับ Resource Restart
ถ้า Transaction สำเร็จและ Commit แล้ว Persistent Data ยังคงอยู่ใน Databaseตามปกติ
ส่วน Lua Cache/Runtime Table ของ Resource จะ Reset เมื่อ Resource Restart
จึงต้องแยก:
Database Commit
=
Persistent
Lua Runtime State
=
Temporary
65. Transaction กับ Server Restart
เช่นเดียวกัน เมื่อ Transaction Commit สำเร็จ ข้อมูลนั้นเป็น Database State
เมื่อ FXServer Restart Resource ควร Load Persistent Data กลับมาตาม Architecture
Transaction ไม่ได้ Persist Lua Variables หรือ Entity Handles
66. Transaction กับ State Bag
State Bag:
Runtime Network State
Transaction:
Persistent Database Consistency
ตัวอย่าง Architecture:
Client Request
↓
Server Validate
↓
Database Transaction
↓
Success
↓
Update Server Cache
↓
Update State Bag
↓
Clients เห็น State ใหม่
ช่วยแยก Persistent Layer กับ Runtime Network Layer ให้ชัดเจน
67. Transaction กับ Entity Creation
อย่าผูก Database Transaction กับ Runtime Entity มากเกินไปจนแยก Recovery ไม่ได้
ตัวอย่าง Persistent Vehicle:
Database Record
↓
Commit
↓
Server สร้าง Runtime Entity
ถ้า Entity Creation ล้มเหลว Database Record ยังสามารถใช้เป็น Source of Truth สำหรับ Retry/Restore ได้
อย่าคิดว่า Runtime NetID เป็นส่วนของ Database Transaction Identity
68. Transaction ช่วย Persistent Vehicle อย่างไร
สมมติการสร้าง Vehicle Record ต้องมี:
vehicles
+
vehicle_settings
พร้อมกัน
ใช้ Transaction ให้สอง Records ถูก Commit เป็นชุด
จากนั้นค่อย:
CreateVehicleServerSetter
สร้าง Runtime Entity หลัง Database Operation สำเร็จ
69. Transaction ใช้กับ Logs ทุกครั้งไหม
ไม่จำเป็น
ถ้า Log เป็น Best-effort Record และไม่ควรทำให้ Business Operation ทั้งหมด Fail อาจไม่ควรรวม Log Query เข้า Transaction หลัก
ตัวอย่าง:
สร้าง Profile สำเร็จ
แต่
Audit Log Insert ล้ม
ต้องถามว่า Requirement จริงคือ:
ควร Rollback Profile ด้วยหรือไม่?
ถ้าไม่ ควรแยก Log ออกจาก Transaction
70. กฎเลือก Query เข้า Transaction
ถามทีละ Query:
ถ้า Query นี้ล้มเหลว
อีก Queries ที่สำเร็จก่อนหน้า
ควรถูกยกเลิกหรือไม่?
ถ้าคำตอบคือ:
ใช่
มีเหตุผลให้อยู่ Transaction เดียวกัน
ถ้า:
ไม่
อาจควรแยก Operation
ตาราง oxmysql Transaction ที่ควรรู้
| เรื่อง | ค่า/พฤติกรรม |
|---|---|
| Function | MySQL.transaction |
| Promise | MySQL.transaction.await |
| Return | Boolean |
| ทุก Query สำเร็จ | Commit |
| มี Query ล้มเหลว | ไม่ Commit ทั้งชุด |
| Isolation ConVar | mysql_transaction_isolation_level |
| Isolation Range | 1–4 |
| Default | 2 — Read Committed |
ข้อมูลนี้ตรงกับ Transaction Documentation ปัจจุบันของ oxmysql.
FiveM MySQL.transaction ใช้อย่างไร
พื้นฐาน:
local queries = {
{
query = [[
INSERT INTO `table_a`
(`value`)
VALUES (?)
]],
values = {
valueA
}
},
{
query = [[
INSERT INTO `table_b`
(`value`)
VALUES (?)
]],
values = {
valueB
}
}
}
local success =
MySQL.transaction.await(queries)
if not success then
print('Transaction failed')
return
end
oxmysql รองรับ Specific Transaction Format และ Return Boolean.
FiveM Transaction failed แก้อย่างไร
ตรวจ:
SQL Syntax
↓
Parameters
↓
Constraints
↓
Column Types
↓
Foreign Keys
↓
Database Connection
↓
Specific Query ที่ Error
แล้วเปิด Database Debug เพื่อดู Query จริง
oxmysql ระบุว่าความเร็ว Query และ Debug Information สามารถดูผ่าน Debug UI/mysql_debug ได้.
FiveM Transaction Commit ครึ่งเดียวได้ไหม
ตาม Guarantee ของ MySQL.transaction ถ้ามีหนึ่ง Query ล้มเหลว Queries ใน Transaction ไม่ควรถูก Commit เป็นบางส่วน.
ถ้าเห็นข้อมูลเหมือนสำเร็จครึ่งเดียว ให้ตรวจว่า:
Queries ทั้งหมดอยู่ Transaction เดียวจริงหรือไม่
มี Query นอก Transaction หรือไม่
Runtime Cache ทำให้ดูเหมือน Database เปลี่ยนหรือไม่
FiveM Transaction ควรใช้กับ Character Creation ไหม
เหมาะหาก Character Creation ต้องสร้างหลาย Records และ Requirement กำหนดว่าต้องมีครบก่อน Character ใช้งานได้
เช่น:
Character
+
Settings
+
Profile Metadata
แต่ Schema จริงควรออกแบบตาม Framework ที่ใช้งาน
FiveM Transaction ควรใช้กับ Player Save ไหม
ใช้ได้เมื่อหลาย Updates ต้องเป็น State เดียวกันจริง
แต่ถ้า Data แต่ละกลุ่ม Save ได้อิสระ ไม่จำเป็นต้องรวมทุกอย่างไว้ Transaction ขนาดใหญ่
FiveM Transaction ทำให้ Server Lag ไหม
ไม่สามารถสรุปจากการใช้ Transaction เพียงอย่างเดียว
Performance ขึ้นกับ Queries ภายใน, Database Hardware, Settings, Version และ Workload และ oxmysql แนะนำดู Query Speed จริงผ่าน Debug Tools.
ถ้าพบ Hitch ให้ใช้ FiveM Profiler และ Database Debug ร่วมกัน
FiveM Transaction Isolation Level ควรตั้งอะไร
oxmysql ปัจจุบันใช้ Default:
2
หรือ:
Read Committed
หากไม่มีเหตุผลด้าน Concurrency ที่ชัดเจน ให้เริ่มจาก Default และอย่าเปลี่ยนเพราะคิดว่าเลข 4 ปลอดภัยกว่าทุกกรณี
FiveM Transaction กับ Prepare เลือกอะไร
ถ้าปัญหาคือ:
หลาย Query ต้อง Commit พร้อมกัน
ใช้ Transaction
ถ้าปัญหาคือ:
Query เดิมถูกเรียกบ่อย
จึงค่อยพิจารณา Prepare ซึ่ง oxmysql ระบุว่าออกแบบมาสำหรับ Frequently-called Queries.
FiveM Transaction ต้องใช้ await ไหม
ไม่จำเป็น เพราะ oxmysql รองรับทั้ง:
Promise/Await
และ:
Callback
สำหรับ Transaction.
เลือกตาม Code Style ของ Resource
FiveM Transaction ปลอดภัยจาก Client Cheat ไหม
Transaction ไม่ได้เป็น Security System
Network Event ยังต้อง Validate ฝั่ง Server โดย Cfx.re แนะนำให้ตรวจข้อมูลเช่น Player State, Position, Permissions และค่า Gameplay สำคัญผ่าน Server-side Methods แทนการเชื่อ Client.
ดังนั้น:
Secure Event
+
Server Validation
+
Database Transaction
ต้องทำงานร่วมกัน
FAQ FiveM MySQL Transaction
FiveM MySQL Transaction คืออะไร
คือการ Execute SQL หลาย Queries เป็นชุด โดย Commit เมื่อทุก Query สำเร็จ และหากหนึ่ง Queryล้มเหลวจะไม่ Commit Queries ใน Transaction.
oxmysql ใช้ Function อะไรสำหรับ Transaction
ใช้:
MySQL.transaction
หรือ Promise Style:
MySQL.transaction.await
MySQL.transaction คืนค่าอะไร
คืน Boolean ระบุผลของ Transaction.
Transaction มี Query ล้มหนึ่งตัวจะเกิดอะไร
oxmysql ระบุว่า Queries ใน Transaction จะไม่ถูก Commit.
Transaction เหมาะกับ Character Creation ไหม
เหมาะหาก Character Creation ต้องสร้างหลาย Persistent Records พร้อมกันและไม่ควรเหลือข้อมูลเพียงบางส่วน
Transaction ทำให้ Query เร็วขึ้นไหม
ไม่ใช่จุดประสงค์หลัก Performance ต้องวัดแยก และ oxmysql ระบุว่า Query Speeds แตกต่างตาม Hardware, Database Settings, Version และ Workload.
Transaction Isolation Level Default เท่าไร
oxmysql ระบุ Default ปัจจุบันเป็น:
2
หรือ Read Committed.
Isolation Level มีค่าอะไรบ้าง
oxmysql รองรับ 1–4: Repeatable Read, Read Committed, Read Uncommitted และ Serializable ตามลำดับ.
Transaction แทน Server Validation ได้ไหม
ไม่ได้ Cfx.re แนะนำให้ Network Events ตรวจข้อมูลสำคัญฝั่ง Server อยู่เสมอ.
Transaction แทน Parameterized Query ได้ไหม
ไม่ได้ Queries ภายใน Transaction ยังควรใช้ Parameter Values แยกจาก SQL Structure
Transaction กับ Insert ต่างกันอย่างไร
MySQL.insert ใช้เพิ่ม Record และคืน Insert ID ส่วน Transaction ใช้หลาย Queries และคืน Boolean ของ Transaction.
Transaction กับ Update ต่างกันอย่างไร
MySQL.update คืนจำนวน Rows ที่ได้รับผล ส่วน Transaction คืน Success/Failure ของชุด Queries.
ประเด็นสำคัญ
FiveM MySQL.transaction ของ oxmysql มีหน้าที่สำคัญคือทำให้ หลาย Database Operations เป็นชุดเดียวกัน โดยจะ Commit เมื่อทุก Query สำเร็จ และถ้ามี Query หนึ่งล้มเหลวจะไม่ Commitชุด Transaction นั้น.
ดังนั้น Transaction เหมาะกับระบบเช่น:
Character
+
Settings
หรือ
Persistent Record
+
Related Metadata
ที่ไม่ควรมีข้อมูลเพียงครึ่งเดียว
แต่ Transaction ไม่ได้แทน:
Server Validation
Parameterized Query
Index
Cache
Backup
Profiler
และไม่ได้มีหน้าที่ทำให้ Query เร็วขึ้นโดยอัตโนมัติ หาก Transaction ช้าให้เปิด mysql_debug/Debug UI แล้วตรวจ Query ภายใน เพราะ oxmysql ระบุว่า Query Performance ขึ้นกับ Hardware, Database Settings, Version และ Current Workload.
ด้าน Security Cfx.re แนะนำให้ Network Events ตรวจข้อมูลสำคัญจาก Server-side Methods และไม่เชื่อ Client Input โดยตรง ดังนั้น Flow ที่เหมาะคือ Client Request → Server Validate → Transaction → Commit → Update Runtime State.
สำหรับผู้อ่าน comsiam ให้จำสูตร “Validate → Transaction → Commit → Update Runtime” และ comsiam แนะนำให้ใช้ Transaction เฉพาะ Queries ที่จำเป็นต้องสำเร็จพร้อมกันจริงๆ ไม่ควรรวม Queries ที่ไม่เกี่ยวข้องเข้า Transaction ใหญ่เพียงชุดเดียว เพราะเป้าหมายของ Transaction คือรักษาความถูกต้องของข้อมูล ไม่ใช่รวมทุก SQL ของ Resource เข้าด้วยกัน
Comments
Post a Comment