← Back to PostgreSQL course
PHASE 1: THE CORE PROBLEM & ARCHITECTURE DAY 3 OF 18 ⚡

Day 3: Transaction Lock Contention & Concurrency Control (การจัดการการเข้าถึงข้อมูลพร้อมกันด้วย Row-level Locks, FOR UPDATE, SKIP LOCKED, NOWAIT และการป้องกัน Deadlock)

เจาะลึกกลไก Explicit Row-level Locking ของ PostgreSQL เมื่อระบบต้องรองรับ Concurrency สูงระดับแสนผู้ใช้ รู้วิธีใช้ FOR UPDATE, NOWAIT, SKIP LOCKED และการตั้งค่า lock_timeout เพื่อขจัดปัญหา Race Condition, Overbooking, คิวล็อกค้างยาว และ Deadlock อย่างหมดจด

📍 ตำแหน่งปัจจุบันของคุณบน Roadmap (18-Day Masterclass) Phase 1: ปราบปัญหา Concurrency & Locks
Day 1: Anonymous Block & Chatty App ✓
➔
Day 2: Storage Engine & Buffer Pool ✓
➔
Day 3: Transaction Lock Contention
➔
Day 4: Architecture Mapping Summary
➔
Day 5: Pillar 1 - UDFs
เปิดบท

ถอดรหัสคำสำคัญของวันนี้

พอคนใช้พร้อมกันเยอะ คอขวดถัดไปไม่ได้อยู่ที่เครือข่ายหรือดิสก์อีกแล้ว แต่เป็นการแย่งกันแตะแถวเดียวกัน จนคิวล็อกสะสม

Transaction Lock Contention

สภาวะที่หลาย transaction พยายามเข้าถึงหรือแก้ข้อมูลชิ้นเดียวกันในเวลาเดียวกัน จนเกิดคิวรอ (block) สะสม ระบบจึงช้าหรือค้าง

Row Locks

แม่กุญแจที่ฐานข้อมูลล็อกไว้เฉพาะแถวที่กำลังถูกอัปเดต เพื่อไม่ให้คำสั่งอื่นมาแก้ทับจนข้อมูลมั่ว

โดยรวมวันนี้คือการดูว่าการล็อกแถวส่งผลให้เกิดคิวค้างอย่างไร และมีไวยากรณ์อะไรบ้าง เช่น FOR UPDATE SKIP LOCKED ที่ช่วยให้ทำงานขนานกันได้โดยไม่ต้องจอดรอแถวที่ยังล็อกอยู่

เปรียบกับชีวิตจริง

เหมือนห้องน้ำที่มีหลายห้องย่อย คนเข้าห้องไหนก็ลงกลอนห้องนั้นไว้ (Row Lock) คนมาทีหลังถ้าอยากเข้าห้องนั้นต้องจอดคิวรอ (Lock Contention) แต่ SKIP LOCKED คือเห็นห้องนี้ล็อกอยู่แล้วเดินไปห้องถัดไปที่ว่างทันที ไม่ต้องยืนรอ

เมื่อผู้ใช้พร้อมกันจำนวนมาก ระบบค้างเพราะติดล็อกได้ง่าย วันนี้จึงสอนให้ควบคุมแม่กุญแจและปลดล็อกเฉพาะจุด เพื่อให้รองรับ high concurrency ได้จริง

1

🏛️ มโนทัศน์ สถาปัตยกรรมองค์กร และสถานการณ์จริง (Concept & Architecture)

เมื่อระบบขยายสเกลรองรับผู้ใช้งานพร้อมกันหลายหมื่น Concurrency คอขวดจะไม่ได้อยู่ที่ Network หรือ Disk I/O อีกต่อไป แต่อยู่ที่ Transaction Lock Contention การที่สองคำสั่งหรือสองผู้ใช้พยายามจะแก้ไขข้อมูลในแถวเดียวกัน (Same Row) ในมิลลิวินาทีเดียวกัน หากไม่มีกลยุทธ์การล็อกที่ถูกต้อง แถวข้อมูลจะถูกล็อกค้าง เกิดคิวรอคอยยาวเป็นโดมิโน และเกิด Deadlock จนเซิร์ฟเวอร์ล่ม

🎫 วิกฤตเปิดขายตั๋วคอนเสิร์ต 100,000 ผู้ใช้: เมื่อ Race Condition & Lock Queue ถล่มระบบ

ในวันเปิดจองบัตรคอนเสิร์ตรอบพิเศษ แฟนเพลง 100,000 คน พยายามกดจองที่นั่งยอดฮิตพร้อมกันในเสี้ยววินาที ทีมพัฒนาเขียนคำสั่งบน App Server อ่านที่นั่งว่างด้วย SELECT ธรรมดา แล้วยิง UPDATE กลับไปโดยไม่มี Explicit Locking Control ผลลัพธ์คือ:

Overbooking 12x
ที่นั่งเดียวถูกขายซ้ำซ้อนให้ลูกค้า 12 คนพร้อมกัน
Too many clients
คิว Lock Wait แช่ค้างจน Connection Pool เต็มระบบ
Deadlock Cascade
ระบบจองตั๋วพังทลายใน 5 นาทีแรกของการเปิดขาย

🚽 เปรียบเทียบกับโลกจริง: การจัดการห้องน้ำสาธารณะที่มีคนมาอั้นรอ 10,000 คน

1. FOR UPDATE ธรรมดา ยืนอั้นรอคิว

คุณเดินไปเคาะประตูห้องน้ำ พบว่ามีคนเข้าอยู่ คุณจึงยืนรอด้านหน้าจนกว่าคนก่อนหน้าจะเปิดประตูออกมา

  • รับประกันไม่ชนกัน 100%
  • แต่ถ้าคนข้างในท้องเสียช้า คนข้างนอกจะต่อคิวล้นออกไปนอกตึก (Lock Queue Cascade)
2. FOR UPDATE NOWAIT ไม่ว่างก็กลับทันที

คุณเดินไปจับลูกบิด ถ้าบิดไม่ไป (มีคนล็อกอยู่) คุณหมุนตัวเดินกลับทันทีใน 0 วินาที ไม่ยอมยืนรอค้าง

  • ไม่มีการยืนแช่คิว Connection ไม่เต็ม
  • แจ้ง Error กลับหาผู้ใช้ทันที (Fail-Fast: 55P03) ให้ผู้ใช้กดเลือกที่นั่งอื่น
3. SKIP LOCKED เดินข้ามไปห้องว่างถัดไป

มีห้องน้ำ 20 ห้อง ห้องที่ 1 ติดล็อกอยู่ คุณไม่รอ และเดินไปเปิดห้องที่ 2 หรือ 3 ที่ว่างอยู่ทันที

  • ไร้การรอคิว 0 มิลลิวินาที (Zero Contention)
  • Worker นับร้อยดึงงานคิวคนละแถวพร้อมกันได้ทันที ไม่ชนกันแม้แต่คู่เดียว
สถาปัตยกรรมระดับลึก: การทำงานของ Lock Queue Cascade และ Deadlock Cycle PostgreSQL Lock Manager
❌ อันตราย: Lock Queue Cascade & Deadlock Cycle ✅ แก้ไข: SKIP LOCKED & Ordered Locking Strategy Transaction A (โอน 1->2) 1. ล็อก Row 1 สำเร็จ 2. นั่งรอคิว Row 2... Transaction B (โอน 2->1) 1. ล็อก Row 2 สำเร็จ 2. นั่งรอคิว Row 1... รอ Row 2 รอ Row 1 ↻ 🚨 วงจรทางตัน: Deadlock Detector ทำงาน • ทั้ง 2 ฝั่งรอซึ่งกันและกันอย่างสมบูรณ์ ไม่มีใครยอมปล่อยก่อน • เมื่อครบเวลา deadlock_timeout (Default 1s) PostgreSQL สั่ง Abort 1 ฝั่ง ERROR: deadlock detected (77P01) ยกเลิก 1 คำสั่งทิ้งทันที! ผลกระทบ: Lock Queue Cascade & Too Many Clients คำสั่งใหม่เข้ามาต้องเข้าคิวต่อท้ายแถวติดล็อก Connection ไม่ถูกคืนเข้า Pool จนกระทั่ง Connection เต็มเพดาน max_connections และระบบจองตั๋วพังทลาย 1. เทคนิค Order-based Locking (ขจัด Deadlock 100%) • บังคับให้ทุก Transaction ล็อกแถวข้อมูลเรียงตาม account_id น้อยไปมากเสมอ • เช่น บัญชี 101 และ 102: ทุกคนต้องล็อก 101 ก่อน แล้วค่อยล็อก 102 • ไม่มีทางเกิดเหตุการณ์ถือสลับกันจนเป็นวงกลม ⚡ ผลลัพธ์: Circular Dependency กลายเป็น 0 ไม่มีวันเกิด Deadlock! 2. เทคนิค FOR UPDATE SKIP LOCKED (Non-Blocking Queue) • Worker A หยิบ Job #1 ➔ ล็อกด้วย FOR UPDATE SKIP LOCKED • Worker B เข้ามาในมิลลิวินาทีเดียวกัน ➔ ข้าม Job #1 ไปหยิบ Job #2 ทันที! • ไม่มี Worker ตัวใดต้องนั่งรอคิว Lock Wait แม้แต่มิลลิวินาทีเดียว ⚡ รองรับ Worker หลายร้อย Threads ทำงานพร้อมกันอย่างเต็มสปีด พร้อมคุม Fail-Fast ด้วย SET LOCAL lock_timeout = '2s';
🎙️ สคริปต์คำพูดสำหรับพรีเซนท์บนเวที (Presentation Pitch Script)

"ทุกท่านครับ คอขวดระดับ Concurrency สูง ไม่ได้เกิดจากฐานข้อมูลไม่มี Index แต่เกิดจาก Transaction Lock Contention ที่ต่างคนต่างแย่งกันถือ Lock ค้างจนเกิด Lock Queue Cascade และ Deadlock

ด้วยการนำเสนอ In-Database Non-Blocking Concurrency Engine ร่วมกับเทคนิค FOR UPDATE SKIP LOCKED และการตั้งค่า lock_timeout เราจะทำให้ Worker Threads นับร้อยรันประมวลผลคิวงานพร้อมกันได้โดยไม่ชนกันเลยแม้แต่มิลลิวินาทีเดียว ขจัดปัญหา Overbooking และป้องกันระบบล่มจาก Deadlock ได้ 100% ครับ"

ถอดรหัสคำศัพท์เทคนิคระดับวิศวกร (Concurrency & Lock Vocab)

🔒 Explicit Row-level Lock (FOR UPDATE)
คำสั่งขอ Exclusive Lock ระดับแถวข้อมูลโดยเฉพาะ เพื่อป้องกันไม่ให้ Transaction อื่นเข้ามาดัดแปลง (UPDATE/DELETE) หรือขอ FOR UPDATE ซ้ำซ้อนจนกว่าจะสั่ง COMMIT หรือ ROLLBACK
⚡ NOWAIT (Fail-Fast Mechanism)
ออปชันเสริมของ FOR UPDATE ที่หากพบว่าแถวข้อมูลนั้นติดล็อกอยู่ จะพ่น Error รหัส 55P03 (lock_not_available) คืนกลับทันทีใน 0 ms เพื่อไม่ให้เกิดการนั่งรอคิวบล็อก Connection
⏭️ SKIP LOCKED (Non-Blocking Concurrency)
ออปชันระดับสูงที่หากพบแถวใดติดล็อกอยู่ จะข้ามแถวนั้นไปอ่านและล็อกแถวถัดไปที่ว่างทันที เหมาะสำหรับการทำ Job Queue / Worker Threads ที่ต้องการความเร็วสูงสุดโดยไม่ชนกัน
⏱️ lock_timeout
การตั้งค่าเวลาสูงสุดที่คำสั่งยอมนั่งรอคิวล็อก (เช่น SET LOCAL lock_timeout = '2s';) หากรอเกินเวลาจะยกเลิกคำสั่งทันที ป้องกันไม่ให้คำสั่งค้างยาวจนล้น Connection Pool
🔄 Deadlock & Circular Dependency
สภาวะทางตันที่ Tx A ถือ Row 1 แล้วรอ Row 2 ขณะที่ Tx B ถือ Row 2 แล้วรอ Row 1 จนติดค้างเป็นวงกลม PostgreSQL จะส่ง Deadlock Detector มา Rollback ฝั่งหนึ่งทิ้งหลังรอครบ deadlock_timeout
🛡️ MVCC Plain SELECT Non-Blocking
กฎเหล็กของ PostgreSQL: "Readers do not block Writers, and Writers do not block Readers" คำสั่ง Plain SELECT ทั่วไปจะอ่าน Snapshot เดิมได้เสมอโดยไม่ถูก FOR UPDATE บล็อก
🧮 วิดเจ็ตทดลอง: ตัวจำลองการแย่งจองงานและคิวล็อก (Concurrency Lock Strategy Simulator)

ลองเลือกกลยุทธ์การล็อก (Lock Strategy) และปรับจำนวน Worker Threads พร้อมกัน เพื่อสังเกตพฤติกรรมการต่อคิว อัตราความสำเร็จ และการเกิด Lock Contention

ผลลัพธ์การทำงานของระบบ (Simulation Outcome):
Worker ทั้งหมดข้ามแถวที่ติดล็อกไปหยิบคิวถัดไปพร้อมกันได้ทันที ไร้การต่อคิว
สำเร็จ 100% (Zero Lock Wait)
2

💻 ปูพื้นฐานไวยากรณ์ & แบบฝึกหัดไต่ระดับ (Row-Level Locking Syntax)

การควบคุม Lock ใน PostgreSQL ต้องเริ่มต้นจากคำสั่ง SELECT ... FOR UPDATE ร่วมกับคำสั่งจัดการ Transaction ใน PL/pgSQL มาดูโครงสร้างไวยากรณ์หลัก 3 รูปแบบที่สถาปนิกและนักพัฒนาต้องเชี่ยวชาญ:

🧱 แกะโครงสร้างไวยากรณ์ 3 รูปแบบของการล็อกระดับแถว

ไวยากรณ์หลัก: FOR UPDATE vs NOWAIT vs SKIP LOCKED PL/pgSQL Concurrency Syntax
-- [รูปแบบที่ 1: FOR UPDATE ธรรมดา - นั่งรอคิวจนกว่าคนก่อนหน้าจะปลดล็อก]
SELECT balance INTO v_balance
FROM accounts
WHERE account_id = 101
FOR UPDATE;

-- [รูปแบบที่ 2: FOR UPDATE NOWAIT - ถ้าติดล็อก ให้พ่น Error 55P03 ทันทีใน 0ms]
SELECT balance INTO v_balance
FROM accounts
WHERE account_id = 101
FOR UPDATE NOWAIT;

-- [รูปแบบที่ 3: FOR UPDATE SKIP LOCKED - ถ้าติดล็อก ให้ข้ามไปอ่านแถวถัดไปทันที]
SELECT task_id, payload INTO v_task_id, v_payload
FROM task_queue
WHERE status = 'PENDING'
ORDER BY priority DESC
LIMIT 1
FOR UPDATE SKIP LOCKED;
💡 ข้อควรจำ: การสั่งล็อกด้วย FOR UPDATE จะมีผลตั้งแต่บรรทัดที่สั่ง จนถึงบรรทัดที่ Transaction จบลง (COMMIT หรือ ROLLBACK) เท่านั้น เมื่อสิ้นสุดบล็อก Locks จะถูกปลดปล่อยอัตโนมัติ

🏋️‍♂️ แบบฝึกหัดไต่ระดับการเขียนจริง (จากระดับเริ่มต้น ➔ Multi-Worker Concurrency)

🟢 Level 1: การใช้ FOR UPDATE เพื่อป้องกัน Race Condition ในการโอนเงิน ระดับเริ่มต้น
DO $$
DECLARE
    -- [1] ประกาศตัวแปรโดยใช้ %TYPE เชื่อมโยงกับตาราง accounts
    v_from_acc   accounts.account_id%TYPE := 101;
    v_to_acc     accounts.account_id%TYPE := 102;
    v_amount     accounts.balance%TYPE    := 500.00;
    v_from_bal   accounts.balance%TYPE;
BEGIN
    -- [2] ล็อกแถวบัญชีต้นทางล่วงหน้าด้วย FOR UPDATE เพื่ออ่านยอดเงินล่าสุด
    -- ป้องกันไม่ให้ Transaction อื่นเข้ามาตัดเงินหรือแก้ไขยอดแทรกกลางคัน
    SELECT balance INTO v_from_bal
    FROM accounts
    WHERE account_id = v_from_acc
    FOR UPDATE; -- ล็อก Row นี้ทันทีจนกว่าจะจบ Block

    -- [3] ตรวจสอบยอดเงินคงเหลือ
    IF v_from_bal >= v_amount THEN
        -- หักเงินบัญชีต้นทาง
        UPDATE accounts SET balance = balance - v_amount WHERE account_id = v_from_acc;
        -- เพิ่มเงินบัญชีปลายทาง
        UPDATE accounts SET balance = balance + v_amount WHERE account_id = v_to_acc;

        RAISE NOTICE 'โอนเงินสำเร็จจำนวน % บาท (ยอดคงเหลือใหม่: %)', v_amount, (v_from_bal - v_amount);
    ELSE
        RAISE NOTICE 'ยอดเงินไม่เพียงพอ (ยอดคงเหลือปัจจุบัน: %)', v_from_bal;
    END IF;
END $$;

-- 📤 ผลลัพธ์ใน Log:
-- NOTICE: โอนเงินสำเร็จจำนวน 500.00 บาท (ยอดคงเหลือใหม่: 4500.00)
จุดที่ต้องสังเกต: ระหว่างที่บล็อกนี้ทำงาน บัญชี 101 จะถูกถือ Exclusive Row Lock ปลอดภัยจากการแย่งตัดเงินซ้ำซ้อน (No Race Condition)
🟡 Level 2: การใช้ SET lock_timeout และ NOWAIT เพื่อทำ Fail-Fast ระดับปานกลาง
DO $$
DECLARE
    v_seat_id seats.seat_id%TYPE := 55;
    v_status  seats.status%TYPE;
BEGIN
    -- [1] ตั้งค่าระยะเวลารอคิวล็อกสูงสุดไม่เกิน 2 วินาที ป้องกันการค้างยาว
    SET LOCAL lock_timeout = '2s';

    -- [2] พยายามจองและล็อกที่นั่ง หากมีคนอื่นกำลังล็อกอยู่ ให้พ่น Error ทันทีด้วย NOWAIT
    SELECT status INTO v_status
    FROM seats
    WHERE seat_id = v_seat_id AND status = 'AVAILABLE'
    FOR UPDATE NOWAIT;

    -- [3] หากดึงผ่าน แสดงว่าจองสำเร็จ ทำการอัปเดตสถานะ
    UPDATE seats SET status = 'RESERVED', reserved_at = CURRENT_TIMESTAMP WHERE seat_id = v_seat_id;
    RAISE NOTICE 'จองที่นั่งหมายเลข % สำเร็จ!', v_seat_id;

EXCEPTION
    -- [4] ดักจับ Exception เมื่อติดล็อกและเกิด NOWAIT/lock_timeout (รหัส Error 55P03)
    WHEN lock_not_available OR 55P03 THEN
        RAISE NOTICE 'ที่นั่งหมายเลข % กำลังมีผู้อื่นทำการจอง กรุณาลองใหม่อีกครั้ง (Fail-Fast สำเร็จ)', v_seat_id;
    WHEN NO_DATA_FOUND THEN
        RAISE NOTICE 'ที่นั่งหมายเลข % ไม่ว่างหรือไม่มีในระบบ', v_seat_id;
END $$;
จุดที่ต้องสังเกต: NOWAIT ร่วมกับบล็อก EXCEPTION ช่วยให้ผู้ใช้ได้รับข้อความแจ้งเตือนทันทีโดยไม่ต้องรอหมุนค้าง 2-8 วินาที

🔴 Level 3: Refactoring โค้ด Job Queue Engine รองรับ Multi-Worker Concurrency (SKIP LOCKED)

เปรียบเทียบโค้ดเดิมที่แย่งงานคิวเดียวกันจนเกิด Race Condition กับโค้ดใหม่ที่ใช้ FOR UPDATE SKIP LOCKED:

❌ โค้ดเดิมฝั่ง App: Worker ดึงงานชนกัน (Race Condition) ชนกันและค้างคิว
# โค้ด Worker บน Application Server
# Worker หลายตัวรันพร้อมกัน ยิงดึงงานคิวตัวเดียวกัน
# ⚠️ Worker 1 และ Worker 2 ได้ task_id เดียวกันไปทำซ้ำ!
task = db.execute(
    "SELECT id, payload FROM tasks "
    "WHERE status = 'PENDING' LIMIT 1"
)
if task:
    # หรือถ้าใส่ FOR UPDATE ธรรมดา Worker ตัวอื่นจะยืนติด Lock รอค้าง
    db.execute(
        "UPDATE tasks SET status = 'PROCESSING' WHERE id = %s",
        (task.id,)
    )
    process_task(task)
วิเคราะห์ข้อเสีย: หากไม่ล็อก งานจะถูกดึงไปทำซ้ำซ้อน แต่ถ้าล็อกแบบธรรมดา Worker ทุกตัวจะแย่งแถวแรก นั่งรอคิวต่อกันจนเสียเวลา
✅ โค้ดใหม่: PL/pgSQL Queue Engine (SKIP LOCKED) Non-Blocking 100%
DO $$
DECLARE
    -- [บรรทัดที่ 1] ประกาศตัวแปรรับค่า ID และ Payload งานที่จะหยิบไปทำ
    v_task_id   tasks.id%TYPE;
    v_payload   tasks.payload%TYPE;
BEGIN
    -- [บรรทัดที่ 2] ดึงงานที่ค้าง โดยใช้ FOR UPDATE SKIP LOCKED
    -- หาก Worker ตัวอื่นกำลังประมวลผลงาน ID ใดอยู่ คำสั่งนี้จะข้ามแถวนั้นไปหยิบงานถัดไปทันที
    SELECT id, payload
    INTO v_task_id, v_payload
    FROM tasks
    WHERE status = 'PENDING'
    ORDER BY priority DESC, created_at ASC
    LIMIT 1
    FOR UPDATE SKIP LOCKED; -- 🎯 ข้ามแถวติดล็อกทันที ทำงานพร้อมกันร้อย Thread โดยไม่ชนกัน

    -- [บรรทัดที่ 3] ตรวจสอบว่ามีงานให้ทำหรือไม่
    IF v_task_id IS NOT NULL THEN
        -- ทำการอัปเดตสถานะงานเป็น PROCESSING บน RAM
        UPDATE tasks
        SET status = 'PROCESSING',
            processed_by = 'Worker-01',
            started_at = CURRENT_TIMESTAMP
        WHERE id = v_task_id;

        RAISE NOTICE 'Worker หยิบงาน ID: % (Payload: %) ไปประมวลผลสำเร็จ', v_task_id, v_payload;
    ELSE
        RAISE NOTICE 'ไม่มีงานค้างในคิว (หรือ งานทั้งหมดกำลังถูก Worker ตัวอื่นทำอยู่)';
    END IF;
END $$;
วิเคราะห์ข้อได้เปรียบ: ไร้การชนกัน 100% และไม่มี Worker ตัวใดต้องนั่งรอคิว สามารถแตก Worker ได้ตามจำนวน CPU Cores อย่างอิสระ
3

📝 คลังข้อสอบและแบบประเมินความรู้ (18 ข้อ Multiple Choices พร้อมเฉลยยิบ)

แบบทดสอบวัดระดับความเข้าใจการจัดการ Concurrency, Explicit Row-level Locks (FOR UPDATE / NOWAIT / SKIP LOCKED), lock_timeout, การป้องกัน Deadlock และข้อสอบทบทวน Day 1 & Day 2 คลิกเลือกคำตอบ ก, ข, ค, หรือ ง เพื่อดูผลลัพธ์และคำอธิบายอย่างละเอียดทันที

📊 สรุปผลการประเมิน Day 3 0 / 18
ความคืบหน้า ตอบแล้ว 0 จาก 18 ข้อ

🎯 สรุปหมุดเชื่อมโยง & ก้าวต่อไปสู่ Day 4

บทเรียน Day 3: Transaction Lock Contention & Concurrency Control แสดงให้เห็นถึงกลไกการควบคุมและป้องกันการชนกันของข้อมูลในระดับ Concurrency สูง โดยใช้การล็อกระดับแถวแบบเจาะจง (FOR UPDATE), กลไก Fail-Fast (NOWAIT / lock_timeout), และกลไกประมวลผลคิวงานแบบ Non-Blocking (SKIP LOCKED) ซึ่งช่วยขจัดปัญหา Race Condition, Overbooking และ Deadlock ได้อย่างเด็ดขาด

🚩 ก้าวต่อไปสู่ Day 4:

Architecture Capstone & Phase 1 Synthesis: เราจะทำการรวบยอดสรุปสถาปัตยกรรม Phase 1 (Days 1–3: Chatty Network RTT, Buffer Pool Memory, และ Transaction Lock Scope) เพื่อสร้าง End-to-End Architectural Benchmark & Presentation Defense Framework เตรียมพร้อมสำหรับการเข้าสู่ Phase 2 (Deep Dive into the 4 Pillars) ครับ!