ความสอดคล้องของข้อมูลใน MongoDB
คนที่มาจากโลกของฐานข้อมูลเชิงสัมพันธ์มักจะคว้า transaction ทันทีที่ต้องเปลี่ยนแปลงข้อมูลมากกว่าหนึ่งอย่าง แต่ใน MongoDB สัญชาตญาณนั้นมักจะพาคุณหลงทาง ฐานข้อมูลให้การรับประกันที่แข็งแกร่งกับคุณฟรี ๆ และการข้ามการรับประกันเหล่านั้นไปหา multi-document transaction เป็นสิ่งที่คุณทำได้ยาก ๆ และอย่างจงใจ ไม่ใช่ทำเป็นค่าเริ่มต้น โมดูลนี้พูดถึงการเข้าใจอย่างชัดเจนว่าคุณมีอะไรอยู่แล้ว สิ่งที่คุณขอเพิ่มได้เมื่อจำเป็นจริง ๆ และวิธีปรับ durability และ visibility ของทุก operation ขึ้นหรือลง
ข้อเท็จจริงเดียวที่ทุกอย่างที่เหลือยึดเกาะอยู่คือ การเขียนไปยัง document หนึ่งฉบับ เป็น atomic เมื่อคุณอัปเดตเรคคอร์ดของสมาชิกคนหนึ่ง MongoDB จะนำการเปลี่ยนแปลงทั้งหมดไปใช้ — แม้จะแตะ nested object หลายฟิลด์ และ array ทั้งหมดในคราวเดียว — เป็นขั้นตอนเดียวที่แบ่งแยกไม่ได้ ไม่มี reader คนอื่นเห็น document ในสภาพที่เปลี่ยนไปครึ่งหนึ่ง และถ้าการเขียนล้มเหลว document จะถูกทิ้งไว้ในสภาพเดิมเป๊ะ คุณไม่ต้องเปิดอะไร ไม่ต้อง commit อะไร และไม่ต้องจ่ายต้นทุนเพิ่มใด ๆ atomicity เป็นเพียงวิธีที่การเขียน document ทำงาน
รูปร่างของความสอดคล้องตรงนี้
หัวข้อที่มีชื่อว่า “รูปร่างของความสอดคล้องตรงนี้”เพราะหนึ่ง document คือหน่วยของ atomicity วิธีที่คุณ ออกแบบโมเดล ข้อมูลจึงเป็นตัวกำหนดว่าความสอดคล้องของคุณจะมาฟรีมากแค่ไหน ข้อมูลที่เปลี่ยนแปลงไปด้วยกันและอยู่ด้วยกันควรอยู่ใน document เดียวกัน เพื่อให้การเขียนครั้งเดียวรักษาความสอดคล้องไว้ได้ เมื่อข้อมูลที่เกี่ยวข้องกันกระจายอยู่ในหลาย document หรือหลาย collection จริง ๆ และต้องเปลี่ยนแปลงเป็นหน่วยเดียวกัน MongoDB มี multi-document transaction ให้ — transaction แบบ ACID จริง ๆ ข้าม session ใช้งานได้จริง แต่มีต้นทุนสูงกว่าและต้องใช้ replica set จึงอยู่ก้นกล่องเครื่องมือมากกว่าจะอยู่บนสุด
ที่ซ้อนทับอยู่บนทั้งสองอย่างคือปุ่มปรับคู่หนึ่งที่ใช้กับทุก operation write concern ควบคุมว่าสมาชิกของ replica set กี่ตัวต้องตอบรับการเขียน และต้อง flush ลง on-disk journal ด้วยหรือไม่ ก่อนที่ driver จะถือว่าการเขียนเสร็จสิ้น — นี่คือปุ่ม durability ส่วน read concern ควบคุมว่าข้อมูลที่คุณอ่านต้องสด และ commit แล้วมากแค่ไหน — นี่คือปุ่ม visibility ทั้งสองอย่างรวมกันช่วยให้คุณแลก latency กับความปลอดภัยได้ทีละ operation
flowchart TD
W["A write request"] --> Q{"How many documents?"}
Q -->|"One document"| A["Atomic automatically — no transaction needed"]
Q -->|"Many documents as a unit"| T["Multi-document transaction in a session"]
A --> WC["Write concern tunes durability"]
T --> WC
WC --> RC["Read concern tunes what readers see"] โมดูลนี้ครอบคลุมอะไรบ้าง
หัวข้อที่มีชื่อว่า “โมดูลนี้ครอบคลุมอะไรบ้าง”- Single-document atomicity — ทำไมการเปลี่ยนแปลงใด ๆ ในหนึ่ง document ลึกแค่ไหนก็ตาม จึงเป็นแบบ all-or-nothing และวิธีออกแบบโมเดลข้อมูลให้สิ่งนี้ครอบคลุมความต้องการส่วนใหญ่ของคุณ
- Multi-document transactions — การเปิด session การรันหลาย operation เป็นหน่วย ACID เดียว และการ commit หรือ abort พร้อมตัวอย่างคลาสสิกของการโอนเงินระหว่างบัญชี
- Read and write concerns — การตั้งค่า
w,jและwtimeoutสำหรับการเขียน ระดับlocal,majorityและlinearizableสำหรับการอ่าน และ read preference สำหรับเลือกว่าสมาชิกตัวไหนเป็นผู้ตอบ - Change streams — การ subscribe ฟีดสด ๆ ของ insert, update และ delete เพื่อให้แอปพลิเคชันของคุณตอบสนองต่อการเปลี่ยนแปลงข้อมูลได้ทันทีที่เกิดขึ้น
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
| ออกแบบ document ให้ single-document atomicity ครอบคลุม | atomic ฟรี ไม่มี session หรือ commit overhead, latency ต่ำสุด | ต้องคิดเรื่อง schema ล่วงหน้า ไม่เหมาะกับข้อมูลที่ต้องเป็น entity อิสระต่อกันจริง ๆ |
| multi-document transaction (ACID เต็มรูปแบบ) | รับประกันความถูกต้องข้าม document และ collection | latency สูงขึ้น ต้องใช้ replica set และเสี่ยง abort ภายใต้ write conflict |
write concern w: "majority" | การเขียนรอดจาก failover เพราะสมาชิกส่วนใหญ่ยืนยันแล้ว | ช้ากว่า w: 1 เพราะต้องรอ replication ไปยังสมาชิกตัวอื่น |
read concern majority | ไม่มี dirty read ไม่มีทางเห็นข้อมูลที่ถูก roll back ภายหลัง | ข้อมูลอาจตามหลังเล็กน้อยเมื่อเทียบกับ local |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- คว้า transaction ทันทีที่ operation แตะมากกว่าหนึ่ง document — โดยไม่ถามก่อนว่าการออกแบบ document ใหม่ให้ข้อมูลที่เปลี่ยนแปลงไปด้วยกันอยู่ใน document เดียวจะตัดความจำเป็นของ transaction ทิ้งไปได้เลยหรือไม่
- ปนกันระหว่าง durability กับ visibility — คิดว่า write concern กับ read concern คือปุ่มเดียวกัน ทั้งที่ write concern ควบคุมว่าการเขียนทนต่อการสูญเสียสมาชิกได้แค่ไหน ส่วน read concern ควบคุมว่าข้อมูลที่อ่านสดและ commit แล้วแค่ไหน คนละแกนกัน
- ตั้งค่า concern ที่แข็งแกร่งที่สุดไว้เป็นค่าเริ่มต้นทุก operation — โดยไม่แยกตาม use case ทำให้ operation ที่ทนต่อความไม่แน่นอนได้เสีย latency ไปฟรี ๆ ทั้งที่ควรเลือกการรับประกันที่อ่อนที่สุดแต่ยังถูกต้องสำหรับงานนั้น
💡 ตัวอย่างจากของจริง
ธนาคาร/fintech ทั่วไป — ใช้ multi-document transaction สำหรับการโอนเงินระหว่างบัญชี เพื่อให้การหักบัญชีต้นทางและการเพิ่มบัญชีปลายทาง commit พร้อมกันเสมอ ไม่มีทางที่เงินจะหายหรืองอกระหว่างทาง
Coinbase — ใช้ทั้ง multi-document transaction และ write concern majority ร่วมกันเมื่อจัดการยอดเงินดิจิทัล เพราะข้อผิดพลาดด้าน consistency ในระบบการเงินมีต้นทุนสูงกว่า latency ที่เพิ่มขึ้นมาก