ข้ามไปยังเนื้อหา

ความสอดคล้องของข้อมูลใน 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 เป็นค่าเริ่มต้น ใช้ transaction เฉพาะเมื่อหลาย document ต้องเปลี่ยนเป็นหน่วยเดียว โดยมี concern ปรับ durability และ visibility
  • 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 เพื่อให้แอปพลิเคชันของคุณตอบสนองต่อการเปลี่ยนแปลงข้อมูลได้ทันทีที่เกิดขึ้น
ตัวเลือกBenefitCost
ออกแบบ document ให้ single-document atomicity ครอบคลุมatomic ฟรี ไม่มี session หรือ commit overhead, latency ต่ำสุดต้องคิดเรื่อง schema ล่วงหน้า ไม่เหมาะกับข้อมูลที่ต้องเป็น entity อิสระต่อกันจริง ๆ
multi-document transaction (ACID เต็มรูปแบบ)รับประกันความถูกต้องข้าม document และ collectionlatency สูงขึ้น ต้องใช้ 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 ที่เพิ่มขึ้นมาก

หน่วยของ atomicity ใน MongoDB โดยค่าเริ่มต้นคืออะไร?
คุณควรคว้า multi-document transaction เมื่อไหร่?
write concern ควบคุมอะไร?