3 นาที

ทำไมการย้าย/มิเกรตฐานข้อมูลถึงกลายเป็นคอขวดสำหรับทีมที่ปล่อยงานเร็ว

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

ทำไมการย้าย/มิเกรตฐานข้อมูลถึงกลายเป็นคอขวดสำหรับทีมที่ปล่อยงานเร็ว

ความหมายของ "คอขวด" ในมิเกรต

มิเกรตฐานข้อมูล คือการเปลี่ยนแปลงใด ๆ ที่คุณนำไปใช้กับฐานข้อมูลเพื่อให้แอปพัฒนาต่อได้อย่างปลอดภัย ซึ่งมักรวมถึง การเปลี่ยนแปลงสกีมา (สร้างหรือแก้ไขตาราง คอลัมน์ ดรรชนี ข้อจำกัด) และบางครั้งรวมถึง การเปลี่ยนแปลงข้อมูล (backfill คอลัมน์ใหม่ แปลงค่า ย้ายข้อมูลไปโครงสร้างใหม่)

มิเกรตจะกลายเป็น คอขวด เมื่อมันชะลอการปล่อยงานมากกว่าโค้ด คุณอาจมีฟีเจอร์พร้อม ทดสอบผ่าน CI/CD เรียบร้อย แต่ทีมต้องรอหน้าต่างมิเกรต การตรวจทานจาก DBA สคริปต์ที่ใช้เวลานาน หรือกฎ "อย่า deploy ในชั่วโมงจราจรสูง" การปล่อยงานไม่ได้ถูกบล็อกเพราะวิศวกรสร้างไม่ได้ แต่ถูกบล็อกเพราะการเปลี่ยนฐานข้อมูลรู้สึกเสี่ยง ช้า หรือคาดเดาไม่ได้

รูปแบบที่เห็นได้ในวงจรปล่อยงาน

รูปแบบทั่วไปได้แก่:

  • การปรับใช้ถูกรออยู่เบื้องหลัง "มิเกรตใหญ่" หนึ่งงานที่แยกไม่ออก
  • ต้องมีหน้าต่างบำรุงรักษาถึงแม้จะเป็นการเปลี่ยนเล็กน้อย
  • หยุดการปล่อยบนโปรดักชันจากความกลัวเรื่องล็อก ไทม์เอาต์ หรือ replication lag
  • เหตุการณ์จากมิเกรตที่รันได้ดีในสเตจิงแต่ล้มเหลวที่สเกลจริง

บทความนี้จะทำอะไร (และไม่ทำ)

นี่ไม่ใช่การสอนทฤษฎีหรือถกเถียงว่า "ฐานข้อมูลแย่" แต่มันคือคู่มือเชิงปฏิบัติว่าทำไมมิเกรตสร้างแรงเสียดทาน และทีมที่ปล่อยบ่อยจะลดปัญหานั้นได้อย่างไรด้วยรูปแบบที่ทำซ้ำได้

คุณจะเห็นสาเหตุเชิงรูปธรรม (เช่น พฤติกรรมล็อก, การ backfill, เวอร์ชันแอป/สกีมาที่ไม่ตรงกัน) และการแก้ไขที่ลงมือได้ (เช่น รูปแบบ expand/contract, roll-forward ที่ปลอดภัย, อัตโนมัติ และเกราะป้องกัน)

ใครควรอ่าน

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

มิเกรตอยู่ตรงไหนในพายไลน์การปล่อย

มิเกรตฐานข้อมูลอยู่ตรงทางวิกฤตระหว่าง "เราทำฟีเจอร์เสร็จ" และ "ผู้ใช้ได้รับประโยชน์แล้ว" โฟลว์ทั่วไปคือ:

Code change → migration → deploy → verify.

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

ที่ที่งานคิวกัน

ทีมที่เร็วยังเจอจุดอุดตันที่คาดได้:

  • การตรวจทาน: การเปลี่ยนสกีมามักต้องตรวจละเอียดกว่า (ดรรชนี ล็อก การ backfill แผนคิวรี่) ทำให้การตรวจทานใช้เวลานานและถูกส่งไปยังผู้ตรวจที่มีทักษะ "ด้านฐานข้อมูล" น้อยคน
  • การรัน: มิเกรตรันบนฐานข้อมูลโปรดักชันหนึ่งชุด (หรือไม่กี่อินสแตนซ์หลัก) ได้พร้อมกันจำกัด
  • การยืนยัน: คุณไม่ได้แค่เช็คว่า "ปรับใช้สำเร็จ" แต่ต้องยืนยันว่าสภาพข้อมูลถูกต้อง เวอร์ชันแอปเข้ากัน และประสิทธิภาพไม่ลดลง

เมื่อขั้นตอนใดช้าลง ทุกอย่างข้างหลังก็ต้องรอ—พีอาร์อื่น การปล่อยอื่น ทีมอื่น

ทำไมมันยากกว่าการทำงานแบบขนานกับโค้ด

โค้ดแอปสามารถปล่อยอยู่หลัง feature flags โรลเอาต์ทีละส่วน หรือปล่อยแยกตามบริการได้ ในทางกลับกัน การเปลี่ยนสกีมาสัมผัสตารางที่ใช้ร่วมและข้อมูลยาวอายุ สองมิเกรตที่แก้ไขตารางยอดนิยมเดียวกันไม่สามารถรันพร้อมกันได้อย่างปลอดภัย และแม้การเปลี่ยนที่ "ไม่เกี่ยวกัน" ก็ยังแย่งทรัพยากร (CPU, I/O, ล็อก)

ต้นทุนของการรอ

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

สาเหตุรากที่พบมากที่สุด

คอขวดมักไม่ได้เกิดจาก "คิวรี่ไม่ดี" เพียงอย่างเดียว แต่เป็นผลจากโหมดความล้มเหลวซ้ำน้อย ๆ ที่โผล่เมื่อทีมปล่อยบ่อยและฐานข้อมูลมีปริมาณจริง

ล็อกที่รันนานและการ rewrite ตาราง

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

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

Backfill ขนาดใหญ่ที่เวลาทำงานคาดการณ์ไม่ได้

การ backfill ข้อมูล (ตั้งค่าคอลัมน์ใหม่ ยกเลิกการทำ normalization เติมข้อมูลเดิม) มักขึ้นกับขนาดตารางและการกระจายข้อมูล สิ่งที่ใช้เวลาวินาทีในสเตจิงอาจใช้เวลาหลายชั่วโมงในโปรดักชัน โดยเฉพาะเมื่อแข่งกับทราฟฟิกจริง

ความเสี่ยงใหญ่คือความไม่แน่นอน: หากไม่สามารถประมาณเวลาทำงานได้อย่างมั่นใจ ก็ไม่สามารถวางแผนหน้าต่างการปล่อยที่ปลอดภัยได้

การพึ่งพาระหว่างสกีมาและเวอร์ชันแอป

เมื่อโค้ดใหม่ต้องการสกีมาทันที (หรือโค้ดเก่าพังกับสกีมาใหม่) การปล่อยจะกลายเป็น "ทั้งหมดหรือไม่มีเลย" การผูกกันเช่นนี้เอาความยืดหยุ่นออก: ไม่สามารถปล่อยแยกกัน, หยุดกลางทางไม่ได้, และการย้อนกลับซับซ้อน

ความต่างของสภาพแวดล้อม (dev/staging/prod ไม่ตรงกัน)

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

ขั้นตอนด้วยมือและความไม่ชัดเจนเรื่องความเป็นเจ้าของ

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

อาการที่ทีมปล่อยเร็วจะสังเกตเห็น

เมื่อมิเกรตเริ่มชะลอทีม สัญญาณแรกมักไม่ใช่ข้อผิดพลาด—แต่เป็นรูปแบบในการวางแผน ปล่อย และกู้คืนงาน

เริ่มมี "หน้าต่างมิเกรต" ในปฏิทิน

ทีมที่ปล่อยเร็วปล่อยเมื่อโค้ดพร้อม ทีมที่ติดคอขวดปล่อยเมื่อฐานข้อมูลว่าง

คุณจะได้ยินว่า "ยังไม่สามารถปรับใช้จนกว่าจะถึงคืนนี้" หรือ "รอหน้าต่างทราฟฟิกต่ำ" และการปล่อยต้องกลายเป็นงานแบตช์ เมื่อเวลาผ่านไป การยึดเปลี่ยนชิ้นงานจะทำให้การปล่อยใหญ่ขึ้นและเสี่ยงขึ้น

ฮอตฟิกถูกบล็อกโดยมิเกรตที่ค้างอยู่

ปัญหาในโปรดักชันเกิด ฟิกซ์เล็ก ๆ พร้อม แต่ปรับใช้ไม่ได้เพราะมีมิเกรตที่ยังไม่เสร็จหรือยังไม่ได้ตรวจทานอยู่ในพายไลน์

นี่คือที่ที่ความเร่งด่วนชนกับการผูกกัน: การเปลี่ยนแปลงแอปและสกีมาผูกแน่นจนอัปเดตที่ไม่เกี่ยวกันก็ต้องรอ ทีมต้องเลือกระหว่างเลื่อนฮอตฟิกหรือเร่งมิเกรตฐานข้อมูล

หลายทีมชนกันบนตารางเดียวกัน

ถ้าหลายสควอดแก้ไขตารางแกนกลางเดียวกัน การประสานงานจะกลายเป็นเรื่องต่อเนื่อง คุณจะเห็น:

  • PR ที่ล้มเหลวต่อเนื่องเพราะมิเกรตไม่สมัคร
  • คำถาม "ใครเป็นเจ้าของตารางนี้?" ในทุกที่ประชุมวางแผน
  • ความขัดแย้งการ merge แบบนาทีสุดท้ายในไฟล์มิเกรต

แม้ทุกอย่างถูกต้องทางเทคนิค ค่าใช้จ่ายจริงคือการจัดลำดับการเปลี่ยนแปลง

การย้อนกลับกลายเป็นปกติ หรือเกิดลูป "deploy เพื่ิอแก้"

การย้อนกลับบ่อยเป็นสัญญาณว่ามิเกรตและแอปไม่เข้ากันในทุกรัฐ ทีมปรับใช้ พบข้อผิดพลาด ย้อนกลับ แก้ แล้วปรับใช้ใหม่—บางครั้งหลายครั้ง

สิ่งนี้เผาผลาญความมั่นใจและกระตุ้นการอนุมัติช้าลง ขั้นตอนด้วยมือมากขึ้น และการเซ็นชื่อเพิ่ม

ผู้เชี่ยวชาญด้าน DB คนเดียวกลายเป็นเกตสำหรับการปล่อย

คนเดียวหรือกลุ่มเล็ก ๆ กลายเป็นผู้ตรวจทานการเปลี่ยนสกีมาทุกครั้ง รันมิเกรตด้วยมือ หรือถูก paged สำหรับเรื่องฐานข้อมูล

อาการไม่ใช่แค่ภาระงาน แต่เป็นการพึ่งพา เมื่อผู้เชี่ยวชาญคนนั้นไม่อยู่ การปล่อยช้าหรือหยุด และคนอื่น ๆ หลีกเลี่ยงการแตะฐานข้อมูลเว้นแต่จำเป็น

ทำไมโปรดักชันทำให้ทุกอย่างยากขึ้น

โปรดักชันไม่ใช่แค่ "staging ที่มีข้อมูลมากกว่า" มันเป็นระบบที่ใช้งานจริงที่มีการอ่าน/เขียน งานแบ็กกราวด์ และผู้ใช้ที่ทำพฤติกรรมไม่คาดคิดพร้อมกัน กิจกรรมนี้เปลี่ยนพฤติกรรมของมิเกรต: การดำเนินการที่เร็วในทดสอบอาจต้องรอหลังคำสั่งจริง หรือล็อกพวกมัน

มิเกรตเล็ก ๆ ก็อาจบล็อกงานสำคัญได้

การเปลี่ยนสกีมาขนาดเล็กหลายอย่างต้องล็อก การเพิ่มคอลัมน์พร้อมค่าเริ่มต้น การ rewrite ตาราง หรือการแตะตารางที่ใช้บ่อยสามารถทำให้ฐานข้อมูลล็อกแถวหรือทั้งตาราง หากตารางนั้นเป็นส่วนทางเดินวิกฤต (เช่น checkout, login, messaging) แม้ล็อกสั้น ๆ ก็สามารถกระจายเป็นไทม์เอาต์ทั่วแอปได้

ดรรชนี ข้อจำกัด และการเปลี่ยนชนิดมีความเสี่ยงสูงกว่า

ดรรชนีและข้อจำกัดช่วยคุณภาพข้อมูลและความเร็วคิวรี แต่การสร้างหรือการตรวจก็มีค่าใช้จ่าย ในโปรดักชันที่มีทราฟฟิก การสร้างดรรชนีอาจแข่งกับทราฟฟิกผู้ใช้เรื่อง CPU และ I/O

การเปลี่ยนชนิดคอลัมน์เสี่ยงเพราะอาจกระตุ้นการ rewrite ทั้งหมด (ตัวอย่างเช่น เปลี่ยนชนิดตัวเลขหรือขยายขนาดสตริงในบาง DB) ซึ่งอาจใช้เวลานาทีหรือชั่วโมงบนตารางใหญ่และล็อกนานกว่าที่คิด

Downtime vs ประสิทธิภาพด้อยลง

“Downtime” คือเมื่อผู้ใช้ไม่สามารถใช้ฟีเจอร์ได้เลย—คำขอล้ม หน้าเว็บ error งานหยุด

“ประสิทธิภาพด้อยลง” แอบแฝง: ระบบยังขึ้นอยู่ แต่ช้าลง คิวสะสม retry เพิ่มขึ้น และมิเกรตที่ ทางเทคนิคสำเร็จ ก็ยังสร้างเหตุเพราะผลักระบบจนเกินขีดจำกัด

ออกแบบมิเกรตสำหรับ Continuous Delivery

ส่งเว็บและมือถือพร้อมกัน
สร้างแอป Flutter ควบคู่ไปกับแบ็กเอนด์เพื่อยืนยันการเปลี่ยนแปลงแบบครบวงจร.

Continuous delivery ทำงานดีที่สุดเมื่อการเปลี่ยนแปลงทุกอย่างปลอดภัยที่จะปล่อยได้ตลอดเวลา มิเกรตมักทำลายสัญญานั้นเพราะมันบังคับการประสานงานแบบ "big bang"

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

รูปแบบสองเฟส: ขยาย → ย้ายข้อมูล → ย่อ

แนวทางปฏิบัติคือรูปแบบ expand/contract (บางครั้งเรียกว่า "parallel change"):

  1. ขยาย: เพิ่มองค์ประกอบสกีมาใหม่โดยไม่ทำให้คิวรีเดิมพัง
  2. ย้ายข้อมูล: backfill หรือแปลงข้อมูลอย่างค่อยเป็นค่อยไป โดยมักใช้แบตช์เล็ก ๆ
  3. ย่อ: เอาคอลัมน์ ข้อจำกัด หรือเส้นทางโค้ดเก่าออกเมื่อมั่นใจว่าทุกอย่างใช้โครงสร้างใหม่แล้ว

สิ่งนี้เปลี่ยนการปล่อยความเสี่ยงสูงให้เป็นหลายขั้นตอนเล็ก ๆ ที่เสี่ยงต่ำ

ความเข้ากันได้ระหว่างการปรับใช้แบบโรลลิง

ระหว่างการปรับใช้แบบโรลลิง บางเซิร์ฟเวอร์อาจยังรันโค้ดเก่าในขณะที่บางตัวรันโค้ดใหม่ มิเกรตของคุณควรสมมติว่าทั้งสองเวอร์ชันยังทำงานพร้อมกัน

นั่นหมายความว่า:

  • โค้ดใหม่ควร backward-compatible กับสกีมาเก่า
  • โค้ดเก่าควร forward-compatible พอที่จะทนการเปลี่ยนสกีมาแบบเพิ่ม (เช่นคอลัมน์ใหม่ที่ยอมเป็น null)

ตัวอย่างชัดเจน: เพิ่ม → backfill → บังคับใช้นโยบาย

แทนการเพิ่มคอลัมน์ NOT NULL พร้อม default (ซึ่งอาจล็อกและ rewrite ตารางใหญ่) ให้ทำแบบนี้:

  • เพิ่มคอลัมน์ nullable
  • ปล่อยโค้ดที่เขียนทั้งฟิลด์เก่าและใหม่ (หรืออ่านโดยมี fallback)
  • Backfill แถวเดิมอย่างปลอดภัยเป็นแบตช์
  • เพิ่มข้อจำกัด (NOT NULL, foreign keys) เมื่อข้อมูลเต็มแล้ว
  • สุดท้ายลบคอลัมน์เก่าและทำความสะอาดโค้ด

ออกแบบแบบนี้ การเปลี่ยนสกีมาจะหยุดเป็นตัวกีดขวางและกลายเป็นงานที่สามารถปล่อยได้ตามปกติ

เทคนิคเพื่อลดความเสี่ยงและเวลาทำงาน

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

เลือกการเปลี่ยนแบบเติมก่อนที่ผลกระทบน้อย

ให้ความสำคัญกับการเปลี่ยนแบบเติม: ตารางใหม่ คอลัมน์ใหม่ ดรรชนีใหม่ ซึ่งมักหลีกเลี่ยงการ rewrite และทำให้โค้ดเดิมยังใช้งานได้ขณะปล่อย

เมื่อจำเป็นต้องเปลี่ยนหรือเอาออก ให้พิจารณาทำเป็นขั้น: เพิ่มโครงสร้างใหม่ ปล่อยโค้ดที่อ่าน/เขียนทั้งสอง แล้วค่อยทำความสะอาดทีหลัง เพื่อให้กระบวนการปล่อยไม่ติดขัดด้วยการตัดขาดครั้งเดียว

แยกงานใหญ่เป็นชิ้นเล็กที่หยุดต่อได้

การอัปเดตใหญ่ (เช่น rewrite ล้านแถว) คือแหล่งกำเนิดคอขวด:

  • แบตช์อัปเดตใหญ่ (เช่น 1,000–10,000 แถวต่อครั้ง) เพื่อลดล็อกยาวและรักษาความตอบสนอง
  • ใช้งานแบ็กกราวด์สำหรับ backfill เมื่อเป็นไปได้ เพื่อให้การปล่อยไม่ต้องรอการ rewrite
  • สำหรับงานหนักของดรรชนีหรือข้อจำกัด ให้เลือกตัวเลือกที่ลดการบล็อก (DB อาจรองรับ "concurrent" หรือ "online" variants)

ทำให้มิเกรตรันซ้ำได้และทนต่อแรงกดดัน

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

ตัวอย่างปฏิบัติ:

  • ตรวจสอบการมีอยู่ก่อนสร้าง/ลบวัตถุ
  • บันทึกความคืบหน้าสำหรับ backfill ยาว ๆ เพื่อให้สามารถทำต่อ
  • หลีกเลี่ยงการผสมการเปลี่ยนสกีมาและการอัปเดตข้อมูลใหญ่ในมิเกรตเดียวกัน

กำหนดกรอบเวลา วัดผล และบังคับขีดจำกัด

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

ถ้ามิเกรตเกินงบ แยกมัน: ปล่อยการเปลี่ยนสกีมาเดี๋ยวนี้ แล้วย้ายงานข้อมูลหนักไปเป็นแบตช์ควบคุม นี่คือวิธีที่ทีมรักษา CI/CD และมิเกรตไม่ให้กลายเป็นเหตุการณ์ซ้ำ

อัตโนมัติและเกราะป้องกันใน CI/CD

ทำให้การปล่อยงานไม่สะดุด
ปล่อยฟีเจอร์โดยไม่ต้องรอการทำงานฐานข้อมูลที่ยาวนานด้วยการแบ่งการเปลี่ยนแปลงเป็นการปล่อยขนาดเล็ก.

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

เช็กก่อนปรับใช้ที่หยุดมิเกรตเสี่ยงตั้งแต่ต้น

ถือไฟล์มิเกรตเหมือนโค้ด: ควรผ่านการเช็กก่อนจะ merge

  • Linting มิเกรต: แจ้งการดำเนินการเสี่ยง (เช่น drop คอลัมน์, rename แบบไม่มีแผน, เพิ่ม non-null โดยไม่มี default) และบังคับมาตรฐานการตั้งชื่อ/ลำดับ
  • Dry runs / แสดงแผน: รันมิเกรตบนฐานข้อมูลใช้แล้วทิ้งเพื่อตรวจไวยากรณ์และสิทธิ์หรือผิด dialect ของ SQL
  • ตรวจสอบการพึ่งพา: ยืนยันว่าสำหรับเวอร์ชันแอปที่จะปรับใช้เข้ากับสถานะสกีมา (เช่น แอปจะไม่เริ่มทำงานโดยต้องการคอลัมน์ที่ยังไม่มี)

เช็กเหล่านี้ควรล้มเหลวเร็วใน CI พร้อมผลที่ชัดเจนเพื่อให้ผู้พัฒนาซ่อมแซมได้โดยไม่ต้องเดา

อัตโนมัติการรันพร้อมมองเห็นได้ชัด

การรันมิเกรตควรเป็นขั้นตอนชั้นหนึ่งในพายไลน์ ไม่ใช่งานรอง

รูปแบบที่ดีคือ: build → test → deploy app → run migrations (หรือสลับขึ้นกับกลยุทธ์ความเข้ากันได้) พร้อม:

  • งานเฉพาะที่ล็อกเริ่ม/สิ้นสุด เวอร์ชัน และเวลาในการรัน
  • แหล่งข้อมูลเดียวสำหรับสิ่งที่รัน (build number, commit SHA)
  • วิธีง่ายสำหรับทุกคนเห็นสถานะ (UI ของพายไลน์ โน้ตการปล่อย หรือหน้า /deployments ภายใน)

เป้าหมายคือลบคำถาม "มิเกรตรันไหม?" ระหว่างการปล่อย

ถ้าคุณสร้างแอปภายในอย่างรวดเร็ว (โดยเฉพาะสแต็ก React + Go + PostgreSQL) แพลตฟอร์มของคุณที่ทำให้วงจร "วางแผน → ปล่อย → กู้คืน" ชัดเจนจะช่วยได้ ตัวอย่างเช่น Koder.ai มีโหมดวางแผนสำหรับการเปลี่ยนแปลง บวกสแนปช็อตและการย้อนกลับ ซึ่งช่วยลดแรงเสียดทานการปฏิบัติเมื่อปล่อยบ่อย—โดยเฉพาะเมื่อหลายคนทำซ้ำบนพื้นผิวผลิตภัณฑ์เดียวกัน

การสังเกตการณ์ระหว่างการเปลี่ยนสกีมา

มิเกรตสามารถล้มเหลวในแบบที่มอนิเตอร์ปกติไม่จับ เพิ่มสัญญาณเฉพาะ:

  • แจ้งเตือนเรื่องเวลามิเกรต การรอคอยล็อก และ replication lag
  • แผงแดชบอร์ดสำหรับ CPU/I/O ของฐานข้อมูลและคิวรีที่รันนานในระหว่างการปล่อย
  • ล็อกเชิงโครงสร้างสำหรับ backfill (แถวที่ประมวลผล อัตรา เวลาโดยประมาณ)

แยก “ปรับใช้แอป” ออกจาก “รัน backfill หนัก”

ถ้ามิเกรตรวม backfill ข้อมูลใหญ่ ให้ทำเป็นขั้นตอนแยกที่ติดตามได้ ปล่อยการเปลี่ยนแปลงโค้ดอย่างปลอดภัยก่อน แล้วรัน backfill เป็นงานควบคุมที่มีการจำกัดอัตราและหยุด/ต่อได้ นี่ช่วยให้การปล่อยยังคงเดินหน้าโดยไม่ซ่อนงานหลายชั่วโมงในช่องติ๊กมิเกรตเดียว

การย้อนกลับ การเลื่อนหน้า และการปล่อยที่ปลอดภัยกว่า

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

แผนย้อนกลับที่แท้จริงประกอบด้วยอะไร

สคริปต์ "down" เป็นแค่ชิ้นส่วนหนึ่งและบ่อยครั้งไม่เชื่อถือได้ แผนย้อนกลับที่ใช้งานได้จริงมักรวม:

  • กลยุทธ์ความปลอดภัยของข้อมูล: สำรองข้อมูล, point-in-time recovery, และหน้าต่างการเก็บรักษาที่ชัดเจน
  • หน้าต่างความเข้ากันได้: แอปเวอร์ชันก่อนหน้ายังรันกับสกีมาใหม่ได้หรือไม่ (และกลับกัน) ชั่วคราว
  • ขั้นตอนปฏิบัติการ: ใครมีสิทธิ์ เขายืนยันอย่างไร และต้องมอนิเตอร์อะไร (อัตราข้อผิดพลาด การเขียนล้มเหลว replication lag)
  • ทริกเกอร์ตัดสินใจ: ขีดจำกัดเฉพาะที่บอกให้หยุด rollout และย้อนกลับ

เมื่อการย้อนกลับไม่ปลอดภัย (และการเลื่อนหน้าดีกว่า)

การเปลี่ยนแปลงบางอย่างย้อนกลับได้ยาก: มิเกรตทำลายข้อมูล, backfill ที่เขียนทับแถว, หรือการเปลี่ยนชนิดที่ไม่สามารถย้อนโดยไม่เสียข้อมูล ในกรณีเหล่านี้ roll-forward ปลอดภัยกว่า: ปล่อยมิเกรตหรือ hotfix ตามมาที่แก้ความเข้ากันได้และข้อมูล แทนการพยายามย้อนเวลา

รูปแบบ expand/contract ช่วยในเรื่องนี้ด้วย: ให้ช่วงเวลาที่อ่าน/เขียนคู่ขนาน แล้วค่อยเอาทางเก่าออกเมื่อมั่นใจ

ฟีเจอร์แฟล็กและการปล่อยเชิงค่อยเป็นค่อยไป

ลด blast radius โดยแยกมิเกรตออกจากการเปลี่ยนพฤติกรรม ใช้ฟีเจอร์แฟล็กเพื่อเปิดอ่าน/เขียนแบบค่อยเป็นค่อยไป และปล่อยแบบก้าวหน้า (ตามเปอร์เซ็นต์ ต่อเทนแนนต์ หรือเป็นกลุ่ม) หากเมตริกพุ่งขึ้น คุณสามารถปิดฟีเจอร์โดยไม่ต้องแตะฐานข้อมูลทันที

ซ้อมย้อนกลับในสเตจิง

อย่ารอเหตุการณ์เพื่อค้นพบขั้นตอนย้อนกลับไม่ครบ ซ้อมมันในสเตจิงด้วยข้อมูลที่สมจริง มี runbook เวลาจริง และแดชบอร์ดมอนิเตอร์ การซ้อมควรตอบคำถามชัดเจน: "เราสามารถกลับสู่สถานะที่เสถียรได้เร็ว และพิสูจน์ได้หรือไม่?"

กระบวนการทีม: ความเป็นเจ้าของ การตรวจทาน และการกำหนดเวลา

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

กำหนดความเป็นเจ้าของ (โดยไม่สร้างคอขวด)

มอบบทบาทชัดเจนสำหรับมิเกรตทุกงาน:

  • ผู้เขียน: โดยปกติผู้พัฒนาฟีเจอร์ที่เข้าใจการเปลี่ยนแปลงและผลต่อผู้ใช้
  • ผู้ตรวจ: เพื่อนร่วมทีมที่ผ่านการฝึกให้สังเกตปัญหาด้านประสิทธิภาพและความปลอดภัย (ไม่จำเป็นต้องเป็น "คนฐานข้อมูล")
  • ผู้อนุมัติ/การยกระดับ: การหมุนเวียนเล็ก ๆ (on-call หรือทีมแพลตฟอร์ม) สำหรับการเปลี่ยนที่มีความเสี่ยงจริงๆ

นี่ช่วยลดการพึ่งพาคนเดียวในฐานข้อมูลแต่ยังให้เครื่อข่ายความปลอดภัยกับทีม

ใช้เช็คลิสต์การตรวจทานมิเกรตแบบเบา

เก็บเช็คลิสต์สั้นพอที่จะใช้งานได้จริง การตรวจทานที่ดีมักครอบคลุม:

  • พฤติกรรมล็อก: จะบล็อกอ่าน/เขียนหรือไม่ แม้เพียงสั้น ๆ?
  • ปริมาณข้อมูล: จะกระทบกี่แถว และอาจใช้เวลากี่นาที/ชั่วโมง?
  • ความเข้ากันได้: แอปเก่าและใหม่รันบนสกีมาในระหว่าง rollout ได้หรือไม่?
  • แผนถอย: คุณจะเลื่อนหน้ายังไงถ้าย้อนกลับไม่ได้?

พิจารณาเก็บเป็นแม่แบบ PR เพื่อความสม่ำเสมอ

กำหนดเวลางานที่เสี่ยงด้วยความตั้งใจ

ไม่ใช่มิเกรตทุกงานต้องมีการประชุม แต่ชิ้นที่เสี่ยงสูงสมควรได้รับการประสาน สร้างปฏิทินแชร์หรือกระบวนการ "หน้าต่างมิเกรต" ที่มี:

  • เจ้าของที่ชื่อชัดเจน,
  • เวลาที่แนะนำ (เมื่อการครอบคลุมซัพพอร์ตดีที่สุด),
  • ลิงก์ไปยัง PR และขั้นตอน rollout.

ถ้าคุณต้องการรายละเอียดเชิงลึกเกี่ยวกับการตรวจสอบความปลอดภัยและการอัตโนมัติ ให้เชื่อมสิ่งนี้เข้ากับกฎ CI/CD ของคุณที่ /blog/automation-and-guardrails-in-cicd.

วัดคอขวดและป้องกันไม่ให้กลับมาอีก

ได้เวลาสร้างเพิ่ม
รับเครดิตโดยแบ่งปันสิ่งที่คุณสร้างบน Koder.ai ผ่านโปรแกรมรับเครดิต.

ถ้ามิเกรตชะลอการปล่อย ให้ปฏิบัติเหมือนปัญหาประสิทธิภาพ: กำหนดว่า "ช้า" คืออะไร วัดมันสม่ำเสมอ และทำให้การปรับปรุงเห็นได้ มิฉะนั้นคุณจะแก้เหตุการณ์เจ็บปวดครั้งเดียวแล้วไถลกลับไปสู่รูปแบบเดิม

ติดตามเมตริกที่ทำนายความเจ็บปวด

เริ่มด้วยแดชบอร์ดเล็ก ๆ (หรือรายงานรายสัปดาห์) ที่ตอบว่า: "มิเกรตใช้เวลาส่งมอบเท่าไร?" เมตริกที่มีประโยชน์รวม:

  • ระยะเวลามิเกรต: เวลารวมที่ใช้รันมิเกรตต่อการปรับใช้ และ p95 ใน 30–90 วันล่าสุด
  • อัตราความล้มเหลว: % ของการปรับใช้ที่มิเกรตล้มเหลว ไทม์เอาต์ หรือต้องแทรกแซงด้วยมือ
  • การปรับใช้ที่ถูกบล็อก: จำนวนการปล่อยที่เลื่อนเพราะมิเกรตกำลังรัน จัดคิว หรือถือว่าเสี่ยง

เพิ่มบันทึกสั้น ๆ ว่า ทำไม มิเกรตช้า (ขนาดตาราง ดรรชนี ล็อก การทำซ้ำ เครือข่าย ฯลฯ) เป้าหมายไม่ใช่ความถูกต้องสมบูรณ์ แต่เพื่อจับผู้กระทำความผิดซ้ำ

บันทึกเหตุการณ์และเกือบเกิดเหตุ (แล้วเปลี่ยนเป็นกฎ)

อย่าแค่เอกสารเหตุการณ์ในโปรดักชัน จับ "เกือบเกิด" ด้วย: มิเกรตที่ล็อกตารางฮอต "ประมาณหนึ่งนาที" การปรับใช้ที่เลื่อน หรือการย้อนกลับที่ไม่ทำงานตามคาด

เก็บบันทึกง่าย ๆ: เกิดอะไร ผลกระทบ ปัจจัยร่วม และขั้นตอนป้องกันที่คุณจะทำครั้งหน้า เมื่อเวลาผ่านไป รายการเหล่านี้จะกลายเป็นรายการ "anti-pattern" ของมิเกรตและช่วยกำหนดค่าเริ่มต้นที่ดีขึ้น (เช่น เมื่อจำเป็นต้อง backfill, เมื่อแยกการเปลี่ยน, เมื่อรันนอกรายการ)

รักษา playbook สำหรับประเภทมิเกรตทั่วไป

ทีมที่เร็วลดความเหนื่อยจากการตัดสินใจโดยการมาตรฐาน สูตรปลอดภัยที่ดีรวม:

  • การเพิ่มคอลัมน์ nullable และการ backfill
  • การสร้างดรรชนีโดยไม่รบกวนงาน
  • การลบ/เปลี่ยนชื่อคอลัมน์ด้วยขั้นตอนความเข้ากันได้
  • มิเกรตข้อมูลขนาดใหญ่ (แบตช์, throttle, checkpoint)

เชื่อม playbook เข้ากับเช็คลิสต์การปล่อยเพื่อให้ใช้ตอนวางแผน ไม่ใช่หลังเกิดปัญหา

อย่าให้ประวัติการมิเกรตกลายเป็นคอขวดเอง

สแต็กบางอย่างช้าลงเมื่อเทเบิลประวัติการมิเกรตและไฟล์เพิ่ม หากพบเวลาเริ่มต้นโปรแกรมช้าลง การตรวจสอบ diff นาน หรือ timeout ของเครื่องมือ ให้วางแผนบำรุงรักษาเป็นระยะ: ตัดหรือเก็บถาวรประวัติการมิเกรตเก่า ตามแนวทางของเฟรมเวิร์กที่ใช้ และยืนยันทดสอบเส้นทาง rebuild สำหรับสภาพแวดล้อมใหม่

เลือกเครื่องมือจัดการการเปลี่ยนฐานข้อมูลที่เร็ว

เครื่องมือไม่สามารถแก้กลยุทธ์มิเกรตที่ผิดได้ แต่เครื่องมือที่เหมาะสมสามารถลดแรงเสียดทาน: ขั้นตอนด้วยมือน้อยลง มองเห็นชัดขึ้น และการปล่อยที่ปลอดภัยขึ้นภายใต้แรงกดดัน

มาตรฐานของเครื่องมือมิเกรตที่ดี

เมื่อประเมินเครื่องมือ จัดลำดับความสำคัญฟีเจอร์ที่ลดความไม่แน่นอนขณะปรับใช้:

  • รองรับ zero-downtime: รูปแบบ expand/contract, การสร้างดรรชนีออนไลน์, การ backfill ที่ปลอดภัย (หรืออย่างน้อยคำแนะนำและการตรวจเช็ก)
  • มองเห็นได้: สถานะชัดเจนว่าอะไรรัน ที่ไหน และเมื่อไร—ต่อสภาพแวดล้อมและต่อเวอร์ชัน
  • การอนุมัติและการแยกหน้าที่: รองรับการรันบนโปรดักชันแบบมีเกทโดยไม่เปลี่ยนทุกการปล่อยให้กลายเป็นคิวตั๋ว
  • ร่องรอยการตรวจสอบ: ล็อกที่ไม่เปลี่ยนแปลงว่าใครอนุมัติ ใครรัน อะไรเปลี่ยน และสคริปต์ที่แน่นอน

ความเข้ากันได้สำคัญกว่ารายการฟีเจอร์

เริ่มจากโมเดลการปรับใช้ของคุณแล้วย้อนกลับ:

  • ถ้าคุณปล่อยหลายบริการเล็ก ๆ คุณต้องการเครื่องมือที่รองรับ มิเกรตระดับบริการ และหลีกเลี่ยงการผูกทีมข้ามกัน
  • ถ้าคุณมีฐานข้อมูลแชร์เดียว คุณต้องการการ ประสานงาน ที่เข้มข้นขึ้น การติดตามการพึ่งพา และอาจการโรลเอาต์เป็นขั้น
  • ถ้าคุณใช้ CI/CD หนัก ๆ ให้ตรวจดูว่าเครื่องมือผสานกับพายไลน์ของคุณอย่างไร: มันรันมิเกรตอัตโนมัติในสภาพแวดล้อมล่าง และต้องการอนุมัติในโปรดักชันไหม?

ตรวจสอบความเป็นจริงการปฏิบัติ: มันทำงานกับข้อจำกัดของเอนจินฐานข้อมูลของคุณได้ไหม (ล็อก, DDL ที่รันนาน, การทำซ้ำ) และให้เอาต์พุตที่ทีม on-call สามารถตอบสนองได้รวดเร็วหรือไม่

ถ้าคุณใช้แนวทางแพลตฟอร์มในการสร้างและปล่อยแอป มองหาความสามารถที่ลดเวลาการกู้คืนเท่ากับเวลาการสร้าง ตัวอย่างเช่น Koder.ai รองรับการส่งออกซอร์สโค้ดพร้อมเวิร์กโฟลว์โฮสติ้ง/ปรับใช้ และโมเดลสแนปช็อต/ย้อนกลับช่วยให้กลับสู่สถานะที่รู้จักได้เร็วเมื่อปล่อยบ่อย

เริ่มเล็กด้วยเครื่องมือทดลอง

อย่าเปลี่ยนเวิร์กโฟลว์ทั้งองค์กรในครั้งเดียว ทดลองเครื่องมือบน บริการหนึ่งหรือเทเบิลที่เปลี่ยนบ่อย

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

ถ้าคุณพร้อมสำรวจตัวเลือกและเส้นทางการออกตัว ให้ดูที่ /pricing หรืออ่านคำแนะนำเพิ่มเติมใน /blog.

คำถามที่พบบ่อย

อะไรทำให้การมิเกรตฐานข้อมูลกลายเป็น “คอขวด” แทนที่จะเป็นขั้นตอนการปรับใช้ปกติ?

การมิเกรตกลายเป็นคอขวดเมื่อต้องชะลอการส่งมอบมากกว่าที่โค้ดจะทำ—เช่น คุณมีฟีเจอร์พร้อม แต่การปล่อยงานต้องรอหน้าต่างบำรุงรักษา สคริปต์ที่ใช้เวลานาน ผู้ตรวจสอบเฉพาะทาง หรือติดกับความกลัวเรื่องล็อก/การดีเลย์ในการทำซ้ำบนโปรดักชัน.

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

มิเกรตสร้างแรงเสียดทานมากที่สุดที่ไหนในกระบวนการ CI/CD?

ส่วนใหญ่พายไลน์จะเป็น: โค้ด → มิเกรต → ปรับใช้ → ตรวจสอบ

แม้โค้ดจะทำงานแบบขนานได้ แต่ขั้นตอนมิเกรตมักไม่เป็นเช่นนั้น:

  • การตรวจทานมักถูกส่งต่อไปยังคนจำนวนน้อยลง
  • มีเพียงเครื่องหลักหนึ่งเครื่อง (หรือไม่กี่เครื่อง) ที่รับการเปลี่ยนแปลงที่มีผลกระทบได้อย่างปลอดภัยในครั้งเดียว
  • การตรวจสอบต้องยืนยันว่าสภาพข้อมูลถูกต้องและประสิทธิภาพไม่ลดลง ไม่ใช่แค่ "การปรับใช้สำเร็จ" เท่านั้น.
สาเหตุทางเทคนิคที่พบบ่อยที่สุดที่ทำให้มิเกรตชะลอทีมที่ปล่อยงานเร็วคืออะไร?

สาเหตุหลักทางเทคนิคที่พบบ่อยได้แก่:

  • การดำเนินการที่ทำให้เกิดล็อกนานหรือการเขียนตารางใหม่ทั้งหมด (การเปลี่ยนชนิดคอลัมน์, ข้อจำกัดบางอย่าง, การสร้างดรรชนีบางประเภท)
  • การทำ backfill ขนาดใหญ่ที่เวลาทำงานขึ้นกับปริมาณข้อมูลในโปรดักชัน
  • การขึ้นต่อกันแน่นระหว่างเวอร์ชันแอปและสกีมา (ไม่มีหน้าต่างความเข้ากันได้)
  • ความต่างของสภาพแวดล้อม (staging ไม่ตรงกับ production เพียงพอ)
  • การดำเนินการด้วยมือและความไม่ชัดเจนเรื่องความเป็นเจ้าของที่ชะลอการตรวจทานและการเปิดใช้
ทำไมมิเกรตที่ทำงานได้ในสเตจกังยังเกิดเหตุบนโปรดักชัน?

โปรดักชันไม่ใช่แค่ “staging ที่มีข้อมูลมากกว่า” แต่มันเป็นระบบที่มีการอ่าน/เขียนจริง งานแบ็กกราวด์ และผู้ใช้ที่ทำพฤติกรรมไม่คาดคิดพร้อมกันเสมอ สิ่งนี้เปลี่ยนพฤติกรรมของมิเกรต: การดำเนินการที่เร็วในทดสอบอาจต้องรอล็อกหลังคำสั่งที่กำลังทำงานจริง หรือแข่งกับคิวคำขออื่น ๆ

  • การเปลี่ยนแปลงเล็ก ๆ อาจยังต้องล็อกตารางที่ใช้งานหนัก
  • งานสร้างดรรชนี/ข้อจำกัดสามารถแข่งขันกับทราฟฟิกผู้ใช้เรื่อง CPU และ I/O
  • สิ่งที่เร็วใน staging อาจช้าบน production เพราะการแย่งชิงทรัพยากรหรือลักษณะการกระจายของข้อมูลต่างกัน

ดังนั้นการทดสอบความสามารถในการสเกลครั้งแรกมักเกิดขึ้นจริงเมื่อมิเกรตบนโปรดักชัน

การให้แอป/สกีมาสามารถเข้ากันได้ระหว่างการปรับใช้แบบโรลลิงต้องทำอย่างไร?

เป้าหมายคือให้เวอร์ชันแอปเก่าและใหม่ทำงานกับสถานะฐานข้อมูลเดียวกันได้อย่างปลอดภัยระหว่างการปรับใช้แบบโรลลิง

ในทางปฏิบัติ:

  • โค้ดใหม่ต้องทนทานต่อสกีมาเก่า (อ่าน/เขียนแบบถอยหลังได้)
  • โค้ดเก่าต้องสามารถทนต่อสกีมาใหม่ได้พอสมควร (มักทำโดยการเพิ่มแบบ "additive" เช่นคอลัมน์ใหม่ที่ยอมเป็น null)

สิ่งนี้ป้องกันการปล่อยแบบ "ทั้งหมดหรือไม่มีเลย" ที่ต้องเปลี่ยนสกีมาและแอปพร้อมกันเป๊ะ ๆ

รูปแบบมิเกรตแบบ expand/contract คืออะไร และควรใช้เมื่อไหร่?

มันเป็นวิธีที่ทำซ้ำได้เพื่อหลีกเลี่ยงการเปลี่ยนแปลงแบบบิ๊กแบง:

  • Expand: เพิ่มองค์ประกอบสกีมาใหม่แบบไม่ทำให้เกิดการแตกหัก (เช่นคอลัมน์ nullable, ตารางใหม่)
  • Migrate data: ทำ backfill/แปลงข้อมูลอย่างค่อยเป็นค่อยไป (ด้วยแบตช์หรืองานแบ็กกราวด์)
  • Contract: เอาคอลัมน์เก่าหรือเส้นทางโค้ดออกเมื่อแน่ใจว่าทุกอย่างย้ายมาใช้โครงสร้างใหม่แล้ว

ใช้ได้เมื่อคุณอยากเปลี่ยนโดยไม่ต้องตัดขาดครั้งเดียว

ทำอย่างไรถึงจะเพิ่มคอลัมน์ NOT NULL โดยไม่ทำให้เกิดล็อกยาวหรือการ rewrite ตาราง?

ลำดับที่ปลอดภัยกว่า:

  • เพิ่มคอลัมน์เป็น nullable ก่อน (หลีกเลี่ยง default ที่ทำให้ต้อง rewrite)
  • ปล่อยโค้ดที่เขียนทั้งฟิลด์เก่าและใหม่ (หรืออ่านด้วย fallback)
  • Backfill แถวที่มีอยู่เป็นแบตช์อย่างปลอดภัย
  • เพิ่ม NOT NULL หรือ foreign key ก็ต่อเมื่อข้อมูลถูกเติมเต็มครบถ้วน
  • เอาคอลัมน์เก่าและทำความสะอาดโค้ดทีหลัง

วิธีนี้ลดความเสี่ยงเรื่องล็อกและการ rewrite ตารางขนาดใหญ่

มีวิธีปฏิบัติที่เป็นไปได้ใดบ้างเพื่อลดเวลาทำงานและความเสี่ยงของมิเกรตภายใต้โหลดโปรดักชัน?

ทำงานหนักให้แบ่งเป็นชิ้นเล็กที่หยุดต่อได้และอยู่นอกเส้นทางปรับใช้หลัก:

  • แบตช์อัปเดตใหญ่ ๆ (เช่น 1,000–10,000 แถวต่อแบตช์) เพื่อลดเวลาล็อก
  • รัน backfill เป็นงานแบ็กกราวด์พร้อมการจำกัดอัตราและความสามารถหยุด/ต่อ
  • เลือกตัวเลือกออนไลน์/พร้อมใช้งานสำหรับดรรชนี/ข้อจำกัดถ้ามี
  • หลีกเลี่ยงการผสมการเปลี่ยนสกีมาและการอัปเดตข้อมูลใหญ่ในมิเกรตเดียวกัน

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

ตรวจสอบ CI/CD และการอัตโนมัติแบบไหนที่ป้องกันมิเกรตที่ "ไม่ดี" ไม่ให้ถึงโปรดักชัน?

ปฏิบัติเหมือนมิเกรตเป็นโค้ดและบังคับใช้เกราะป้องกัน:

  • Linting เพื่อตรวจจับการดำเนินการเสี่ยง (drop, rename ที่ไม่มีแผน, เพิ่ม non-null โดยไม่มีแผน)
  • Dry runs บนฐานข้อมูลที่ใช้แล้วทิ้งเพื่อตรวจไวยากรณ์และสิทธิ์ก่อน
  • ตรวจสอบความเข้ากันได้/การพึ่งพา ว่าเวอร์ชันแอปที่จะปรับใช้นั้นเข้ากับสถานะสกีมาได้หรือไม่
  • ขั้นตอนพายไลน์เฉพาะที่มีล็อกชัดเจน (เริ่ม/สิ้นสุด, เวอร์ชัน, เวลาในการรัน)

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

เมื่อไหร่ควรย้อนกลับกับเมื่อไหร่ควรเลื่อนหน้า (roll-forward) หลังเกิดปัญหามิเกรต?

ให้ความสำคัญกับกระบวนการ ไม่ใช่แค่สคริปต์ "down":

  • มิเกรตบางอย่างย้อนกลับไม่ได้อย่างปลอดภัย (การเขียนทับข้อมูลทำลายข้อมูล, การเปลี่ยนชนิดที่ไม่สามารถกลับได้) ดังนั้นการ roll-forward มักปลอดภัยกว่า: ปล่อยมิเกรตตามมาเพื่อคืนค่าความเข้ากันได้หรือแก้ข้อมูล แทนการพยายามย้อนเวลา
  • รักษา "หน้าต่างความเข้ากันได้" เพื่อให้เวอร์ชันแอปก่อนหน้ายังรันกับสกีมาใหม่ได้สั้น ๆ
  • ใช้ฟีเจอร์แฟล็กเพื่อแยกการเปลี่ยนพฤติกรรมออกจากการเปลี่ยนสกีมา
  • กำหนดทริกเกอร์สำหรับหยุดการปล่อย (อัตราข้อผิดพลาด, การรอคอยล็อก, lag ของการทำซ้ำ) และซ้อม runbook ในสเตจิง

วิธีนี้ช่วยให้การปล่อยยังคงกู้คืนได้โดยไม่ต้องหยุดการเปลี่ยนแปลงฐานข้อมูลทั้งหมด

Related posts