การดู : 0

12/04/2026 18:16น.

JS2GO EP.45 การจัดการ Transactions และ ACID ใน Go และ JavaScript

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
IsolationTransaction ต้องไม่รบกวนกัน
Durabilityเมื่อ commit แล้ว ข้อมูลถูกบันทึกถาวรแม้ระบบล่ม

 

ตัวอย่างโอนเงิน 1,000 บาท

  1. หักจากบัญชี A
  2. เพิ่มในบัญชี B
  3. บันทึก 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 CommittedDirty Readค่าเริ่มต้นของ PostgreSQL
Repeatable ReadNon-repeatable Readหนักขึ้นแต่ปลอดภัยขึ้น
SerializablePhantom Readปลอดภัยที่สุด แต่เกิด Deadlock บ่อยสุด

 

PostgreSQL default = READ COMMITTED
เหมาะสำหรับ 90% ของระบบ production

 

⭐ 4) Deadlock คืออะไร?

 

Deadlock = สถานการณ์ที่ Transaction 2 ตัว “รอซึ่งกันและกัน” ทำให้ไม่มีใครไปต่อได้

 

ตัวอย่าง:

T1T2
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