กลับไปหน้าบทความ
#MongoDB#Schema Migration#Backup#Rollback#Database

ย้ายโครงสร้างข้อมูล MongoDB อย่างปลอดภัยด้วย Backup และ Rollback

การเปลี่ยนโครงสร้างข้อมูลใน MongoDB ต้องเริ่มจากแผนสำรองและการกู้คืน ไม่ใช่เริ่มจากสคริปต์ migration เพียงอย่างเดียว บทความนี้อธิบายแนวทางย้ายข้อมูลแบบค่อยเป็นค่อยไป พร้อมการควบคุมความเสี่ยงในระบบ production

1 สิงหาคม 2569อ่านประมาณ 2 นาที

แชร์บทความ

ย้ายโครงสร้างข้อมูล MongoDB อย่างปลอดภัยด้วย Backup และ Rollback

MongoDB เปิดโอกาสให้เราเปลี่ยนรูปแบบข้อมูลได้อย่างยืดหยุ่น เช่น เปลี่ยน field จาก string เป็น object หรือแยกข้อมูลไปยัง collection ใหม่ แต่เมื่อฐานข้อมูลมีหลายล้าน document และระบบยังให้บริการผู้ใช้อยู่ การเปลี่ยน schema จะกลายเป็นเรื่องของความต่อเนื่องทางธุรกิจ ไม่ใช่เพียงงานเขียนโค้ด

ก่อนคิดว่าจะใช้คำสั่งใดในการ migrate ทีมควรตอบให้ได้ก่อนว่าจะย้อนระบบกลับอย่างไรหากเกิดปัญหา เป้าหมายของ migration ที่ดีจึงไม่ใช่แค่ทำให้ข้อมูลเปลี่ยนรูปสำเร็จ แต่ต้องทำให้ระบบยังทำงานได้และธุรกิจไม่สะดุดระหว่างทางด้วย

เริ่มจากแผน Backup และ Restore

คำถามแรกคือเราต้องสำรองข้อมูลในขอบเขตใด เพราะแต่ละทางเลือกมีต้นทุนและวิธีกู้คืนแตกต่างกัน ทีมควรพิจารณาว่าต้อง backup ทั้งฐานข้อมูล เฉพาะ collection ที่เกี่ยวข้อง หรือเก็บเฉพาะข้อมูลก่อนถูก migration

การมี dump ล่าสุดเพียงอย่างเดียวอาจไม่เพียงพอสำหรับระบบ production สิ่งสำคัญกว่าคือการพิสูจน์ว่าไฟล์สำรองนั้น restore กลับมาใช้งานได้จริง โดยควรทดสอบใน staging และตรวจสอบองค์ประกอบที่เกี่ยวข้องอย่างเป็นระบบ เช่น

  • ข้อมูลและ collection ที่จำเป็นกลับมาครบถ้วน
  • Index ถูกสร้างกลับมาครบและอยู่ในสถานะพร้อมใช้งาน
  • User, role และ permission ยังคงถูกต้อง
  • View และกระบวนการภายนอกที่ทำหน้าที่คล้าย trigger ยังทำงานร่วมกันได้
  • การตั้งค่าหรือทรัพยากรที่อยู่นอกขอบเขตของ dump ได้รับการสำรองด้วยวิธีอื่น

เครื่องมืออย่าง mongodump และ mongorestore เหมาะกับงานพื้นฐานหลายประเภท แต่ทีมต้องเข้าใจข้อจำกัดของมันด้วย หากข้อมูลมีขนาดใหญ่ การ dump อาจใช้เวลานาน และถ้าระบบยังเขียนข้อมูลตลอดเวลา backup ที่ได้อาจไม่ใช่ snapshot ที่นิ่งและสอดคล้องกันเพียงพอ

สำหรับระบบที่ต้องการความต่อเนื่องสูง อาจต้องใช้ snapshot จาก replica set หรือระบบสำรองของบริการ managed เช่น MongoDB Atlas วิธีเหล่านี้ช่วยรองรับ point-in-time recovery ทำให้ทีมย้อนฐานข้อมูลไปยังช่วงเวลาที่ต้องการได้ แทนที่จะมีเพียงไฟล์สำรองก้อนหนึ่งที่ไม่รู้ว่ากู้คืนได้ละเอียดแค่ไหน

ออกแบบ Migration ให้ย้อนกลับได้

หลายทีมเขียนสคริปต์แปลงข้อมูลแบบทางเดียว เช่น แยก fullName เป็น firstName และ lastName ปัญหาคือข้อมูลอาจถูกแยกผิด หรือ API เวอร์ชันใหม่อาจมีข้อผิดพลาดหลัง deploy หากข้อมูลถูกเปลี่ยนรูปไปแล้ว การ rollback เฉพาะโค้ดอาจไม่ช่วย เพราะโค้ดเก่าอาจอ่านโครงสร้างใหม่ไม่ได้

ก่อนลงมือจึงต้องกำหนดระดับความสามารถในการย้อนกลับให้ชัดเจน รวมถึงระบุ cut-off point ว่าช่วงใดสามารถ rollback โค้ดได้ทันที ช่วงใดต้องรัน reverse migration และช่วงใดจำเป็นต้อง restore จาก backup หรือประกาศ maintenance

ใช้แนวทาง Expand and Contract

แนวทางที่ปลอดภัยกว่าการเปลี่ยน schema ในครั้งเดียวคือ expand and contract ซึ่งเปิดให้รูปแบบเก่าและใหม่อยู่ร่วมกันชั่วคราว แม้ว่าวิธีนี้จะใช้เวลามากกว่า แต่ช่วยลดความเสี่ยงและทำให้แต่ละขั้นตอนตรวจสอบได้ก่อนเดินหน้าต่อ

ลำดับการทำงานโดยทั่วไปประกอบด้วยขั้นตอนต่อไปนี้

  1. เพิ่ม field หรือโครงสร้างข้อมูลใหม่โดยยังไม่ลบของเดิม
  2. ปรับระบบให้เขียนทั้งรูปแบบเก่าและรูปแบบใหม่ชั่วคราว
  3. Backfill ข้อมูลเดิมเป็นชุด ๆ จนครบ
  4. ปรับ reader ให้อ่านรูปแบบใหม่ โดยยังรองรับข้อมูลเก่าระหว่างช่วงเปลี่ยนผ่าน
  5. ตรวจสอบความถูกต้อง ประสิทธิภาพ และผลกระทบต่อผู้ใช้
  6. ลบ field หรือโครงสร้างเก่าในรอบ deploy ถัดไปเมื่อมั่นใจแล้ว

หัวใจของวิธีนี้คือการแยกการเปลี่ยนแปลงขนาดใหญ่ออกเป็นหลายขั้นที่ย้อนกลับง่าย ทีมจึงสามารถหยุดหรือชะลอ migration ได้ทันทีเมื่อพบความผิดปกติ โดยไม่ต้องเดิมพันกับการเปลี่ยนข้อมูลทั้งหมดในครั้งเดียว

ทำให้สคริปต์รันซ้ำได้อย่างปลอดภัย

Migration script ที่ดีควรมีคุณสมบัติ idempotent หรือสามารถรันซ้ำได้โดยไม่ทำให้ข้อมูลเสียหาย หากเครือข่ายขาด กระบวนการหยุดทำงาน หรือสคริปต์สำเร็จเพียงครึ่งเดียว ทีมควรเริ่มงานต่อได้โดยไม่สร้างข้อมูลซ้ำหรือแปลง document เดิมจนค่าผิดเพี้ยน

วิธีควบคุมสถานะอาจใช้ marker อย่าง migratedAt หรือ schemaVersion รวมถึงตรวจสอบเงื่อนไขก่อน update ทุกครั้ง Marker เหล่านี้ช่วยให้สคริปต์แยกได้ว่า document ใดดำเนินการแล้ว รายการใดควรถูกข้าม และรายการใดต้องลองใหม่

นอกจากการป้องกันการประมวลผลซ้ำแล้ว ควรเก็บรายละเอียดของข้อผิดพลาดแยกตาม document ด้วย การรู้ว่าเอกสารใดล้มเหลวและล้มเหลวเพราะอะไรช่วยให้แก้ไขเฉพาะจุดได้ โดยไม่ต้องเริ่ม migration ทั้งหมดใหม่

ตรวจสอบ Index และ Query Pattern

การเปลี่ยนโครงสร้างข้อมูลมักทำให้รูปแบบ query เปลี่ยนตามไปด้วย Field ใหม่อาจต้องมี index เพิ่ม ขณะที่ compound index เดิมอาจไม่รองรับเงื่อนไขหรือการเรียงลำดับแบบใหม่แล้ว หากมองเฉพาะความถูกต้องของข้อมูล ทีมอาจพบปัญหาประสิทธิภาพหลัง migration โดยไม่ทันตั้งตัว

ก่อนเริ่มงานควรใช้ explain() ตรวจสอบ execution plan กับชุดข้อมูลที่มีปริมาณและการกระจายใกล้เคียง production การทดสอบกับ document เพียงไม่กี่สิบรายการไม่สามารถสะท้อนต้นทุนของ full collection scan การใช้หน่วยความจำ หรือประสิทธิภาพของ index เมื่อข้อมูลจริงมีหลายล้านรายการได้

ควรวางแผนการสร้าง index ให้สัมพันธ์กับ migration ด้วย เพราะการสร้าง index บน collection ขนาดใหญ่อาจเพิ่มภาระให้ฐานข้อมูล ทีมจึงต้องติดตามทั้งเวลา query การใช้ทรัพยากร และผลกระทบต่อ workload ปกติระหว่างดำเนินงาน

แบ่งงานเป็น Batch และติดตามความคืบหน้า

การ update ทั้ง collection ในครั้งเดียวโดยไม่มีการควบคุมอาจทำให้ฐานข้อมูลรับภาระหนัก เกิด lock contention หรือกระทบ latency ของคำขอจากผู้ใช้ วิธีที่ปลอดภัยกว่าคือแบ่งข้อมูลเป็น batch จำกัดความเร็ว และปรับขนาด batch ตามความสามารถของระบบ

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

  • processed: จำนวนรายการที่ตรวจสอบแล้ว
  • updated: จำนวนรายการที่แปลงสำเร็จ
  • skipped: จำนวนรายการที่ไม่ต้องแก้ไขหรือเคย migrate แล้ว
  • failed: จำนวนรายการที่ดำเนินการไม่สำเร็จ

ควรกำหนดเกณฑ์สำหรับหยุดงานอัตโนมัติด้วย เช่น error rate สูงเกินค่าที่รับได้ latency ของระบบเพิ่มขึ้น หรือ replica lag สูงผิดปกติ เมื่อมีทั้ง log และตัวชี้วัดที่ชัดเจน ทีมจะตัดสินใจหยุด ลดความเร็ว หรือดำเนินงานต่อได้จากข้อมูลจริง

รักษาความเข้ากันได้ของ Application

ระหว่าง migration ฐานข้อมูลอาจมีเอกสารรูปแบบเก่าและใหม่อยู่พร้อมกัน โค้ดจึงควรอ่านได้ทั้งสองรูปแบบชั่วคราว และ validation layer ต้องไม่เข้มงวดจนปฏิเสธข้อมูลเดิมก่อนการ backfill เสร็จสมบูรณ์

API response ต้องรักษาความเข้ากันได้กับ client ที่ยังใช้งานอยู่ด้วย ประเด็นนี้สำคัญเป็นพิเศษสำหรับ mobile app เพราะไม่สามารถบังคับให้ผู้ใช้ทุกคนอัปเดตหรือ rollback แอปได้พร้อมกับ backend การเปลี่ยน contract จึงควรใช้วิธีเพิ่มข้อมูลใหม่ก่อน และยกเลิกข้อมูลเก่าภายหลังเมื่อ client รุ่นเก่าหมดอายุการใช้งานแล้ว

หากระบบใช้การเขียนข้อมูลสองรูปแบบพร้อมกัน ทีมควรกำหนดแหล่งข้อมูลหลักให้ชัดเจนและตรวจสอบความสอดคล้องระหว่าง field เก่ากับ field ใหม่อย่างต่อเนื่อง วิธีนี้ช่วยป้องกันไม่ให้ข้อมูลสองชุดค่อย ๆ แตกต่างกันจนไม่สามารถตัดระบบไปใช้ schema ใหม่ได้อย่างมั่นใจ

ซ้อม Migration และกำหนดจุดตัดสินใจ

แผนที่ยังไม่เคยทดลองไม่ควรถูกมองว่าเป็นแผนพร้อมใช้งาน ทีมควร restore backup ใน staging แล้วรัน migration ด้วยข้อมูลที่ใกล้เคียง production เพื่อวัดระยะเวลา ตรวจสอบความถูกต้อง และค้นหาปัญหาที่อาจเกิดขึ้นก่อนวันจริง

Runbook ควรระบุขั้นตอน migration และ rollback อย่างชัดเจน รวมถึงผู้รับผิดชอบ วิธีตรวจสอบความสำเร็จ เกณฑ์หยุดงาน และช่องทางสื่อสารเมื่อเกิดเหตุ นอกจากนี้ต้องกำหนดด้วยว่าจุดใดยัง rollback ได้ง่าย จุดใดจำเป็นต้อง restore และสถานการณ์แบบใดต้องเข้าสู่ maintenance mode

หลังการซ้อมควรนำระยะเวลาจริงและปัญหาที่พบมาปรับแผน ไม่ควรประเมินหน้าต่าง migration จากความเร็วของสคริปต์เพียงอย่างเดียว เพราะยังต้องเผื่อเวลาสำหรับ validation การสร้าง index การตรวจสอบระบบ และการกู้คืนเมื่อผลลัพธ์ไม่เป็นไปตามคาด

สรุป

MongoDB ไม่ได้บังคับ schema แบบแข็งตายตัว แต่ความยืดหยุ่นนั้นทำให้ทีมต้องมีวินัยในการเปลี่ยนแปลงมากขึ้น ทุก migration ควรเริ่มจาก backup ที่พิสูจน์แล้วว่า restore ได้ ตามด้วยการเปลี่ยน schema แบบค่อยเป็นค่อยไป สคริปต์ที่รันซ้ำได้ และการติดตามผลระหว่างทาง

ทางถอยที่ดีไม่ใช่เครื่องหมายของความไม่มั่นใจ แต่เป็นส่วนหนึ่งของการออกแบบระบบ production เมื่อทีมรู้ว่าจะหยุดตรงไหน ย้อนกลับอย่างไร และกู้ข้อมูลจากจุดใดได้ การเปลี่ยนโครงสร้างข้อมูลก็จะเกิดขึ้นโดยมีความเสี่ยงที่ควบคุมได้และไม่ทำให้ธุรกิจสะดุด