12/04/2026 18:16น.

JS2GO EP.45 การจัดการ Transactions และ ACID ใน Go และ JavaScript
#Node.js
#JavaScript
#Golang
#Go
#ACID
#Transactions
Transaction คือชุดของคำสั่งหลายคำสั่งที่ “ต้องสำเร็จทั้งหมด หรือไม่สำเร็จเลย”
เหมาะกับงานที่ต้องการความถูกต้องของข้อมูล 100% เช่น:
- หักเงิน + บันทึกประวัติ + ตัด Stock
- โอนเงิน 2 บัญชี
- ย้ายข้อมูลจาก Order → Shipment
- อัปเดตหลายตารางพร้อมกัน
ถ้าคำสั่งใดคำสั่งหนึ่งล้มเหลว → ต้อง ROLLBACK ทั้งหมด ไม่เช่นนั้นข้อมูลจะ เสียหาย (inconsistent) และระบบจะเกิดปัญหนักในระยะยาว
EP.45 นี้จะพาคุณเข้าใจ:
✔ ACID Properties
✔ การจัดการ Deadlock
✔ Go Transaction Pattern (pgx + GORM)
✔ Node.js Transaction Pattern (pg + PostgreSQL)
✔ Business Transaction vs SQL Transaction
✔ Best Practices สำหรับ Production
⭐ 1) ACID Properties เสาหลักของระบบฐานข้อมูลที่เชื่อถือได้
ACID คือคุณสมบัติสำคัญที่ทุก Transaction ต้องมี
| Property | ความหมาย |
|---|---|
| Atomicity | สำเร็จทั้งหมด หรือไม่สำเร็จเลย |
| Consistency | หลังจบ transaction ข้อมูลยังคงถูกต้องตาม constraint |
| Isolation | Transaction ต้องไม่รบกวนกัน |
| Durability | เมื่อ commit แล้ว ข้อมูลถูกบันทึกถาวรแม้ระบบล่ม |
ตัวอย่างโอนเงิน 1,000 บาท
- หักจากบัญชี A
- เพิ่มในบัญชี B
- บันทึก Log
ถ้า “เพิ่มเงินในบัญชี B” ล้มเหลว → ต้อง rollback ทั้งหมด
⭐ 2) SQL Transaction Flow มาตรฐาน
ใช้กับ PostgreSQL / MySQL ทั้งหมด
BEGIN;
-- do something
COMMIT;
-- or
ROLLBACK;
⭐ 3) Isolation Level เรื่องสำคัญที่ Dev มักละเลย
Isolation Level มี 4 ระดับ:
| ระดับ | ปัญหาที่ป้องกันได้ | หมายเหตุ |
|---|---|---|
| Read Uncommitted | ไม่มี | แทบไม่เคยใช้ |
| Read Committed | Dirty Read | ค่าเริ่มต้นของ PostgreSQL |
| Repeatable Read | Non-repeatable Read | หนักขึ้นแต่ปลอดภัยขึ้น |
| Serializable | Phantom Read | ปลอดภัยที่สุด แต่เกิด Deadlock บ่อยสุด |
PostgreSQL default = READ COMMITTED
เหมาะสำหรับ 90% ของระบบ production
⭐ 4) Deadlock คืออะไร?
Deadlock = สถานการณ์ที่ Transaction 2 ตัว “รอซึ่งกันและกัน” ทำให้ไม่มีใครไปต่อได้
ตัวอย่าง:
| T1 | T2 |
|---|---|
| LOCK row A | |
| LOCK row B | |
| LOCK row B | |
| LOCK row A |
วิธีแก้ที่ถูกต้อง
✔ ตรวจจับ error code และ retry
✔ ทำ lock ตามลำดับเดียวกันทุกที่ (lock ordering)
✔ ใช้ SELECT … FOR UPDATE อย่างระวัง
✔ ตั้ง statement_timeout
⭐ 5) SQL Transaction vs Business Transaction
SQL Transaction
ใช้กับคำสั่งภายใน Database เดียวกัน เช่น:
- โอนเงิน
- อัปเดตหลายฟิลด์
- CRUD แบบหลายตาราง
Business Transaction
ประกอบด้วยหลาย Service เช่น:
- Payment
- Inventory
- Shipping
- Email Notification
จึงต้องใช้ Patterns เช่น:
✔ Saga Pattern
✔ Outbox Pattern
✔ Event-driven workflow
⭐ 6) การใช้งาน Transaction ใน Go (pgx)
pgx = เร็วที่สุด และคุม logic ได้ลึกที่สุด เหมาะกับ Production
Basic Transaction
tx, err := DB.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx)
_, err = tx.Exec(ctx, "UPDATE users SET ...")
if err != nil {
return err
}
return tx.Commit(ctx)
รูปแบบสมบูรณ์ + Retry Deadlock (แนะนำ)
func Transfer(ctx context.Context, from, to string, amount int) error {
for retry := 0; retry < 3; retry++ {
tx, err := DB.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx)
// Step1: deduct
if _, err := tx.Exec(ctx,
"UPDATE accounts SET balance = balance - $1 WHERE id=$2",
amount, from,
); err != nil {
// deadlock code 40P01
continue
}
// Step2: add
if _, err := tx.Exec(ctx,
"UPDATE accounts SET balance = balance + $1 WHERE id=$2",
amount, to,
); err != nil {
continue
}
if err := tx.Commit(ctx); err == nil {
return nil
}
}
return errors.New("transaction failed after retries")
}
⭐ 7) Transaction ใน Go (GORM)
เหมาะกับงาน CRUD และ Dev ที่ต้องการเขียนง่าย ๆ
func Transfer(db *gorm.DB, from, to string, amount int) error {
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&Account{}).
Where("id = ?", from).
Update("balance", gorm.Expr("balance - ?", amount)).
Error; err != nil {
return err
}
if err := tx.Model(&Account{}).
Where("id = ?", to).
Update("balance", gorm.Expr("balance + ?", amount)).
Error; err != nil {
return err
}
return nil
})
}
GORM จะ handle commit/rollback ให้โดยอัตโนมัติ
⭐ 8) Transaction ใน Node.js (pg)
Basic Transaction
const client = await pool.connect();
try {
await client.query("BEGIN");
await client.query("UPDATE accounts SET balance = balance - $1 WHERE id=$2", [1000, "A"]);
await client.query("UPDATE accounts SET balance = balance + $1 WHERE id=$2", [1000, "B"]);
await client.query("COMMIT");
} catch (err) {
await client.query("ROLLBACK");
throw err;
} finally {
client.release();
}
Node.js พร้อม Retry Deadlock (สำคัญมาก)
async function transfer(from, to, amount) {
for (let attempt = 0; attempt < 3; attempt++) {
const client = await pool.connect();
try {
await client.query("BEGIN");
await client.query(
"UPDATE accounts SET balance = balance - $1 WHERE id=$2",
[amount, from]
);
await client.query(
"UPDATE accounts SET balance = balance + $1 WHERE id=$2",
[amount, to]
);
await client.query("COMMIT");
return;
} catch (err) {
await client.query("ROLLBACK");
if (err.code === "40P01") continue; // Deadlock
throw err;
} finally {
client.release();
}
}
throw new Error("transaction failed after retries");
}
⭐ 9) Transaction Scopes
| Scope | ควรใช้หรือไม่ |
|---|---|
| Single Query | ❌ ไม่ต้องใช้ |
| Simple Multi-step | ✔ ใช้ |
| Complex Domain Logic | ✔ รวม logic ในฟังก์ชันเดียว |
| Distributed | ใช้ Saga / Outbox |
⭐ 10) Best Practices สำหรับ Production
✔ 1) ตั้ง Statement Timeout ป้องกันการค้างเป็นนาที
✔ 2) Retry on Deadlock ตรวจ code 40P01 แล้ว retry
✔ 3) ใช้ Short-lived Transaction ไม่ทำ logic หนักภายใน transaction
✔ 4) Logging ทุกครั้งที่ Commit/Rollback ช่วย Debug ปัญหาง่ายขึ้น
✔ 5) ใช้ Idempotency สำหรับ operation ที่ retry ได้ ป้องกัน double processing
✔ 6) ระวัง SELECT FOR UPDATE ใช้ผิด = deadlock
📌 สรุป
Transaction คือกลไกหลักที่ปกป้องข้อมูลของระบบจากความผิดพลาด
ใน EP.45 คุณได้เรียนรู้ตั้งแต่พื้นฐาน → Production:
✔ ACID + Isolation Levels
✔ Deadlock Handling
✔ Go (pgx + GORM) Patterns
✔ Node.js Transaction Patterns
✔ Best Practices ทั้งหมดที่ระบบจริงต้องมี
เมื่อคุณเข้าใจ Transaction ระดับนี้ ระบบของคุณจะ ปลอดภัย เสถียร และไม่มีข้อมูลเสียหาย แม้รองรับโหลดสูง
🔵 EP.46 Middleware & Modular Architecture ใน Go และ Node.js
คุณจะได้เรียนรู้:
- Middleware (Auth, Logging, Rate Limit)
- Service / Repository Pattern
- โปรเจกต์โครงสร้างที่ใช้จริงใน Production