การเลือกฐานข้อมูล — SQL vs NoSQL
Database Selection · ACID · BASE · CAP · ▶ ในคลิป 0:06:20 – 0:12:40
ฐานข้อมูลคือการตัดสินใจที่ เปลี่ยนทีหลังแพงที่สุด ในระบบ — โค้ดแอป refactor ได้ทุกสัปดาห์ แต่การย้ายข้อมูล production ข้ามชนิดฐานข้อมูลคือโปรเจกต์ระดับไตรมาส คำถามหลักของบทนี้คือ: งานของเราต้องการ "ความถูกต้องเคร่งครัด" หรือ "ความยืดหยุ่นและสเกล" มากกว่ากัน
2.1 Relational Database (SQL) — โครงสร้างคือจุดแข็ง
ฐานข้อมูลเชิงสัมพันธ์ (PostgreSQL, MySQL) เก็บข้อมูลเป็น ตารางที่มี schema ตายตัว — ทุกแถวมีคอลัมน์เหมือนกัน และตารางเชื่อมโยงกันผ่าน foreign key ทำให้ JOIN ข้อมูลข้ามตารางได้ในคำสั่งเดียว
จุดขายที่แท้จริงของ SQL คือ ACID — สัญญาว่าธุรกรรม (transaction) จะถูกต้องเสมอ:
- A — Atomicity: ทุกขั้นในธุรกรรมสำเร็จทั้งหมด หรือไม่เกิดขึ้นเลย (โอนเงิน: หักบัญชี A และเข้าบัญชี B ต้องเกิดคู่กัน ห้ามค้างครึ่งทาง)
- C — Consistency: ข้อมูลไม่ละเมิดกฎที่ตั้งไว้ (ยอดเงินติดลบไม่ได้, FK ต้องชี้ไปที่แถวที่มีจริง)
- I — Isolation: ธุรกรรมที่รันพร้อมกันไม่เห็นข้อมูลครึ่ง ๆ กลาง ๆ ของกันและกัน
- D — Durability: commit แล้วข้อมูลไม่หาย แม้ไฟดับวินาทีถัดมา
2.2 NoSQL — สี่ตระกูล สี่โจทย์
"NoSQL" ไม่ใช่ฐานข้อมูลชนิดเดียว แต่เป็นคำเรียกรวมของฐานข้อมูลที่ สละความเคร่งครัดของ schema และ JOIN เพื่อแลกกับความยืดหยุ่นและการสเกลแนวนอน (scale-out) แบ่งเป็น 4 ตระกูลหลัก:
2.3 Consistency ที่ต้องแลก — BASE และ CAP
NoSQL ส่วนใหญ่สเกลแนวนอนได้ดีเพราะยอมผ่อนความ consistent ลง จากโมเดล ACID ไปเป็น BASE (Basically Available, Soft state, Eventually consistent) — เขียนข้อมูลที่ node หนึ่งแล้ว node อื่นอาจเห็นค่าใหม่ "ในอีกครู่หนึ่ง" ไม่ใช่ทันที
เบื้องหลังคือ CAP Theorem: ในระบบกระจาย (distributed) เมื่อ network ขาดจากกัน (Partition) เราต้องเลือกอย่างใดอย่างหนึ่งระหว่าง
- C — Consistency: ทุก node ตอบค่าล่าสุดเสมอ (ยอมตอบ error ถ้าไม่ชัวร์)
- A — Availability: ตอบได้เสมอ (ยอมตอบค่าที่อาจเก่า)
ระบบธนาคารเลือก C (ยอดเงินผิดไม่ได้) ระบบ feed โซเชียลเลือก A (เห็นโพสต์ช้าไป 2 วินาทีไม่มีใครเดือดร้อน) — ไม่มีคำตอบถูกสากล มีแต่คำตอบที่ถูกกับโจทย์
2.4 ตารางตัดสินใจ
| มิติ | SQL (Relational) | NoSQL |
|---|---|---|
| โครงสร้างข้อมูล | Schema ตายตัว ตรวจสอบเข้ม | ยืดหยุ่น เปลี่ยนรูปร่างได้ต่อแถว |
| ความสัมพันธ์ | JOIN ได้ในตัว เหมาะข้อมูลโยงกันมาก | ไม่มี JOIN จริง ต้อง denormalize (ทำซ้ำข้อมูล) |
| Transaction | ACID เต็มรูปแบบ | ส่วนใหญ่ BASE / eventual consistency |
| การสเกล | ถนัด scale-up (เครื่องใหญ่ขึ้น) — scale-out ทำได้แต่ยาก | ออกแบบมาเพื่อ scale-out ตั้งแต่ต้น |
| Query | SQL ทรงพลัง วิเคราะห์เฉพาะหน้า (ad-hoc) ได้ดี | Query ตามรูปแบบที่ออกแบบไว้ล่วงหน้า |
| เหมาะกับ | การเงิน, คำสั่งซื้อ, ระบบที่ผิดไม่ได้ | Feed, log, IoT, แคตตาล็อก, ทราฟฟิกมหาศาล |
🧭 หลักเลือกในชีวิตจริง — เริ่มที่ PostgreSQL ไว้ก่อนถ้าไม่แน่ใจ — ระบบส่วนใหญ่ในโลกไม่เคยโตถึงจุดที่ SQL สเกลไม่ไหว และ Postgres ยุคใหม่ทำงาน document (JSONB) ได้ด้วย ค่อยแยกบางส่วนไป NoSQL เมื่อเจอโจทย์เฉพาะจริง ๆ เช่น cache (Redis), เขียน log มหาศาล (Cassandra) — ระบบใหญ่จริงมักใช้ หลายฐานข้อมูลผสมกัน (polyglot persistence) ตามจุดแข็งของแต่ละตัว
2.5 Transaction ข้ามหลายระบบ — Saga, 2PC และ Outbox
ACID ที่เล่าไปข้างต้นใช้ได้เมื่อข้อมูลทั้งหมดอยู่ใน ฐานข้อมูลเดียว แต่ระบบแบบ microservices มักตัดขั้นตอนหนึ่ง (เช่น "สั่งซื้อ") ออกเป็นหลายบริการที่ต่างคนต่างมีฐานข้อมูลของตัวเอง — สั่งซื้อสำเร็จต้อง "สร้างออเดอร์" + "ตัดเงิน" + "จองสต็อก" ให้ครบทั้งสามที่ ถ้าที่ใดที่หนึ่งล้มเหลว ทำอย่างไรไม่ให้ข้อมูลค้างครึ่ง ๆ กลาง ๆ ข้ามระบบ
Two-Phase Commit (2PC) — ความถูกต้องเข้มแต่บล็อกทั้งระบบ
วิธีตรงไปตรงมาคือยกเครื่องมือของ ACID ข้ามระบบ: coordinator ถาม Phase 1 (Prepare) ให้ทุกฝ่าย "เตรียมพร้อม" และล็อกทรัพยากรไว้ก่อน ถ้าทุกฝ่ายตอบ "ได้" coordinator จึงสั่ง Phase 2 (Commit) พร้อมกัน — ได้ความถูกต้องระดับเดียวกับ ACID แต่ราคาที่จ่ายคือ blocking: ทุกฝ่ายต้องถือล็อกรอจนกว่า coordinator จะตัดสินใจ ถ้า coordinator ตายกลางคันหรือฝ่ายใดฝ่ายหนึ่งตอบช้า ทุกระบบที่เกี่ยวข้องค้างพร้อมกันหมด — ยิ่งมีบริการเยอะ โอกาสค้างยิ่งสูง จึงแทบไม่มีใครใช้ 2PC ข้าม microservices ในระบบที่ต้องการ availability สูง
Saga Pattern — ทำสำเร็จทีละขั้น แล้ว "แก้คืน" ถ้าพลาด
Saga เปลี่ยนโจทย์จาก "ทำทุกที่พร้อมกันแบบ atomic" เป็น ลำดับ local transaction ทีละขั้น — แต่ละขั้น commit จริงในฐานข้อมูลของตัวเอง แล้วส่ง event บอกขั้นถัดไปให้ทำงานต่อ ถ้าขั้นใดขั้นหนึ่งล้มเหลว จะไม่มีการ "ROLLBACK ข้ามระบบ" แต่ใช้ compensating transaction — ธุรกรรมที่ทำหน้าที่ "แก้คืน" ผลของขั้นที่สำเร็จไปแล้วย้อนตามลำดับ (เช่น ตัดเงินไปแล้วแต่จองสต็อกไม่ได้ → สั่ง "คืนเงิน" แทนการ rollback)
มีสองสไตล์การจัดลำดับ:
- Choreography — ไม่มีตัวกลาง แต่ละ service ฟัง event แล้วตัดสินใจทำงานต่อเอง (event-driven ล้วน) เหมาะ flow สั้น ๆ ไม่กี่ขั้น แต่พอ flow ซับซ้อนขึ้นจะไม่มีที่เดียวที่เห็น "ภาพรวมทั้งหมด" — debug ยาก
- Orchestration — มี "Saga Orchestrator" กลางคอยสั่งทีละขั้นและรู้ทั้ง flow ชัดเจน debug ง่ายกว่า แต่ orchestrator เองก็กลายเป็นจุดที่ต้องดูแลเรื่อง SPOF และความพร้อมใช้งาน (บทที่ 3)
Transactional Outbox — แก้ปัญหา "เขียน DB สำเร็จ แต่ event หลุด"
ทุกแพตเทิร์นข้างต้นพึ่งพา "ส่ง event ให้บริการถัดไปรู้" แต่การเขียนลง database กับการส่ง message ไปคนละระบบกัน — เขียนสำเร็จแต่ส่ง event ไม่สำเร็จ (หรือกลับกัน) คือ dual-write problem ทางแก้มาตรฐานคือ Outbox Pattern: เขียนทั้ง "ข้อมูลจริง" และ "event ที่จะส่ง" ลงตาราง outbox ใน transaction เดียวกัน (atomic เพราะเป็น DB เดียวกัน) แล้วให้ process แยกต่างหากอ่านตาราง outbox แล้วค่อยส่งเข้า message queue จริง — event จะถูกส่งก็ต่อเมื่อ transaction หลักสำเร็จเท่านั้น ไม่มีทางหลุดครึ่งทาง (รายละเอียดของ message queue ที่รับช่วงต่อ อยู่ในบทที่ 9)
| มิติ | 2PC | Saga |
|---|---|---|
| Consistency | Strong — atomic ข้ามระบบจริง | Eventual — ถูกต้องในที่สุด ระหว่างทางอาจเห็นสถานะค้าง |
| Availability | ต่ำ — บล็อกรอทุกฝ่าย | สูง — แต่ละ service ทำงานอิสระ |
| ความซับซ้อน | ตรรกะง่าย แต่ operational เสี่ยง (deadlock/ค้าง) | ต้องออกแบบ compensating tx + รับมือ retry/idempotency เอง |
| เหมาะกับ | ระบบปิดที่ควบคุมทุกฝ่ายเอง ทนบล็อกได้ | Microservices ที่ต้องการ availability สูง |
✅ สรุปบทที่ 2 — SQL = โครงสร้าง + ACID + JOIN เหมาะกับข้อมูลที่ผิดไม่ได้ · NoSQL = ยืดหยุ่น + scale-out + eventual consistency เหมาะกับปริมาณและความเร็ว · CAP บอกว่าตอน network มีปัญหา ต้องเลือกระหว่างตอบถูกเสมอ (C) กับตอบได้เสมอ (A) · ธุรกรรมข้ามหลาย service เลือก Saga (เร็ว+พร้อมใช้งานสูง) เหนือ 2PC (บล็อก) เกือบทุกกรณีใน microservices · คำถามแรกเวลาสัมภาษณ์: "ข้อมูลนี้ยอมเก่าได้ไหม และอ่าน-เขียนหนักด้านไหน"