การย้ายฐานข้อมูล: เปลี่ยนหนอนผีเสื้อให้เป็นผีเสื้อ

เผยแพร่แล้ว: 2022-03-11

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

การย้ายฐานข้อมูล: เปลี่ยนหนอนผีเสื้อให้เป็นผีเสื้อ

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

ในบทความนี้ เราจะมาดูวิธีการใช้การโยกย้ายสคีมาฐานข้อมูลเชิงสัมพันธ์และวิธีเอาชนะปัญหาที่เกิดขึ้นพร้อมกัน

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

ในการเริ่มต้น ให้สำรวจสิ่งที่เรามีในกล่องเครื่องมือของเราเพื่อหลีกเลี่ยงการคิดค้นสิ่งที่ทำได้ดีอยู่แล้ว

เครื่องมือ

ในทุกภาษาที่ใช้กันอย่างแพร่หลาย มีไลบรารีที่ช่วยให้การย้ายฐานข้อมูลทำได้ง่าย ตัวอย่างเช่น ในกรณีของ Java ตัวเลือกยอดนิยมคือ Liquibase และ Flyway เราจะใช้ Liquibase มากขึ้นในตัวอย่าง แต่แนวคิดนี้ใช้กับโซลูชันอื่นๆ และไม่ได้เชื่อมโยงกับ Liquibase

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

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

การย้ายฐานข้อมูลทั่วไป

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

 <?xml version="1.0" encoding="UTF-8"?> <databaseChangeLog> <changeSet author="demo"> <createTable tableName="PRODUCT"> <column name="ID" type="BIGINT"> <constraints primaryKey="true" primaryKeyName="PK_PRODUCT"/> </column> <column name="CODE" type="VARCHAR(50)"> <constraints nullable="false" unique="true" uniqueConstraintName="UC_PRODUCT_CODE"/> </column> </createTable> </changeSet> </databaseChangeLog>

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

 <sql>UPDATE product SET code = 'new_' || code</sql>

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

  1. สร้างตารางใหม่สำหรับผลิตภัณฑ์ด้วย createTable เช่นเดียวกับที่เราเห็นก่อนหน้านี้ ในขั้นตอนนี้ เป็นการดีกว่าที่จะสร้างข้อจำกัดให้น้อยที่สุด มาตั้งชื่อตารางใหม่กัน PRODUCT_TMP
  2. เติม PRODUCT_TMP ด้วย SQL ในรูปแบบของ INSERT INTO ... SELECT ... โดยใช้ sql change
  3. สร้างข้อจำกัดทั้งหมด ( addNotNullConstraint , addUniqueConstraint , addForeignKeyConstraint ) และดัชนี ( createIndex ) ที่คุณต้องการ
  4. เปลี่ยนชื่อตาราง PRODUCT เป็นบางอย่างเช่น PRODUCT_BAK Liquibase สามารถทำได้ด้วย renameTable
  5. เปลี่ยนชื่อ PRODUCT_TMP เป็น PRODUCT (อีกครั้งโดยใช้ renameTable )
  6. ทางเลือก ลบ PRODUCT_BAK ด้วย dropTable

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

หากคุณถือว่า XML, JSON หรือ YAML แปลกประหลาดเกินไปสำหรับงานอธิบายการเปลี่ยนแปลง ให้ใช้ SQL ธรรมดาและใช้คุณลักษณะเฉพาะของผู้จำหน่ายฐานข้อมูลทั้งหมด นอกจากนี้ คุณสามารถใช้ตรรกะแบบกำหนดเองใดๆ ใน Java ธรรมดาได้

วิธีที่ Liquibase ยกเว้นคุณจากการเขียน SQL เฉพาะฐานข้อมูลจริงอาจนำไปสู่ความมั่นใจสูง แต่คุณไม่ควรลืมเกี่ยวกับนิสัยใจคอของฐานข้อมูลเป้าหมายของคุณ เช่น เมื่อคุณสร้างคีย์นอก ดัชนีอาจจะถูกสร้างขึ้นหรือไม่ก็ได้ ขึ้นอยู่กับระบบการจัดการฐานข้อมูลเฉพาะที่ใช้ ด้วยเหตุนี้ คุณอาจพบว่าตัวเองอยู่ในสถานการณ์ที่น่าอึดอัดใจ Liquibase อนุญาตให้คุณระบุว่าชุดการเปลี่ยนแปลงควรทำงานเฉพาะสำหรับฐานข้อมูลบางประเภทเท่านั้น เช่น PostgreSQL, Oracle หรือ MySQL ทำให้เป็นไปได้โดยใช้ชุดการเปลี่ยนแปลงที่ไม่เชื่อเรื่องพระเจ้าของผู้ขายชุดเดียวกันสำหรับฐานข้อมูลที่แตกต่างกัน และสำหรับชุดการแก้ไขอื่นๆ โดยใช้รูปแบบและรูปแบบเฉพาะของผู้ขาย ชุดการเปลี่ยนแปลงต่อไปนี้จะถูกดำเนินการก็ต่อเมื่อใช้ฐานข้อมูล Oracle:

 <changeSet dbms="oracle" author="..."> ... </changeSet>

นอกจาก Oracle แล้ว Liquibase ยังรองรับฐานข้อมูลอื่นๆ อีกสองสามตัวที่พร้อมใช้งานทันที

การตั้งชื่อวัตถุฐานข้อมูล

ทุกอ็อบเจ็กต์ฐานข้อมูลที่คุณสร้างจำเป็นต้องตั้งชื่อ คุณไม่จำเป็นต้องระบุชื่อสำหรับวัตถุบางประเภทอย่างชัดเจน เช่น สำหรับข้อจำกัดและดัชนี แต่ไม่ได้หมายความว่าวัตถุเหล่านั้นจะไม่มีชื่อ ชื่อของพวกเขาจะถูกสร้างขึ้นโดยฐานข้อมูลอยู่แล้ว ปัญหาเกิดขึ้นเมื่อคุณต้องการอ้างอิงวัตถุนั้นเพื่อวางหรือแก้ไข ดังนั้นจึงเป็นการดีกว่าที่จะตั้งชื่อให้ชัดเจน แต่มีกฎเกี่ยวกับชื่อที่จะให้หรือไม่? คำตอบสั้น ๆ คือ มีความสม่ำเสมอ เช่น หากคุณตัดสินใจตั้งชื่อดัชนีดังนี้: IDX_<table>_<columns> ดังนั้นควรตั้งชื่อดัชนีสำหรับคอลัมน์ CODE ดังกล่าว IDX_PRODUCT_CODE

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

การจัดระเบียบชุดการเปลี่ยนแปลง

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

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

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

  1. การโยกย้ายอิสระที่ไม่ส่งผลต่อระบบที่ทำงานอยู่ โดยปกติแล้ว การสร้างตาราง ลำดับ ฯลฯ ใหม่จะปลอดภัย หากแอปพลิเคชันที่ปรับใช้ในปัจจุบันยังไม่ทราบ
  2. การปรับเปลี่ยนสคีมาที่เปลี่ยนโครงสร้างของร้านค้า เช่น การเพิ่มหรือวางคอลัมน์และดัชนี ไม่ควรนำการเปลี่ยนแปลงเหล่านี้ไปใช้ในขณะที่แอปพลิเคชันรุ่นเก่ากว่ายังคงใช้งานอยู่ เนื่องจากอาจนำไปสู่การล็อกหรือการทำงานที่แปลกประหลาดเนื่องจากการเปลี่ยนแปลงในสคีมา
  3. การโยกย้ายอย่างรวดเร็วที่แทรกหรืออัปเดตข้อมูลจำนวนเล็กน้อย หากมีการใช้งานหลายแอพพลิเคชั่น ชุดการเปลี่ยนแปลงจากหมวดหมู่นี้สามารถดำเนินการได้พร้อมกันโดยไม่ทำให้ประสิทธิภาพของฐานข้อมูลลดลง
  4. การย้ายข้อมูลอาจช้าซึ่งแทรกหรืออัปเดตข้อมูลจำนวนมาก การเปลี่ยนแปลงเหล่านี้จะดีกว่าที่จะใช้เมื่อไม่มีการโยกย้ายที่คล้ายกันอื่น ๆ

การแสดงกราฟิกของสี่ประเภท

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

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

ไม่ว่าจะเลือกขั้นตอนใดเพื่อใช้การย้ายข้อมูลก็ตาม ควรสังเกตว่าการใช้ฐานข้อมูลเดียวกันสำหรับหลายแอปพลิเคชันอาจทำให้เกิดการล็อกเมื่อมีการใช้การย้ายข้อมูล Liquibase (เช่นเดียวกับโซลูชันอื่นๆ ที่คล้ายกัน) ใช้ตารางพิเศษสองตารางเพื่อบันทึกข้อมูลเมตา: DATABASECHANGELOG และ DATABASECHANGELOGLOCK อันแรกใช้สำหรับเก็บข้อมูลเกี่ยวกับเซ็ตการแก้ไขที่ใช้ และอันหลังเพื่อป้องกันการโยกย้ายพร้อมกันภายในสคีมาฐานข้อมูลเดียวกัน ดังนั้น หากหลายแอปพลิเคชันต้องใช้สกีมาฐานข้อมูลเดียวกันด้วยเหตุผลบางประการ ควรใช้ชื่อที่ไม่ใช่ค่าเริ่มต้นสำหรับตารางข้อมูลเมตาเพื่อหลีกเลี่ยงการล็อก

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

ตัวอย่างองค์กรชุดการเปลี่ยนแปลง

ขึ้นอยู่กับข้อกำหนดการใช้งานเฉพาะอย่างมาก แต่ประเด็นต่อไปนี้มักจะสมเหตุสมผล:

  1. จัดกลุ่มบันทึกการเปลี่ยนแปลงตามการเปิดตัวผลิตภัณฑ์ของคุณ สร้างไดเร็กทอรีใหม่สำหรับแต่ละรีลีส และวางไฟล์บันทึกการเปลี่ยนแปลงที่เกี่ยวข้องลงในไดเร็กทอรี มีบันทึกการเปลี่ยนแปลงรูทและรวมบันทึกการเปลี่ยนแปลงที่สอดคล้องกับรุ่นต่างๆ ในบันทึกการเปลี่ยนแปลงการเผยแพร่ ให้รวมบันทึกการเปลี่ยนแปลงอื่นๆ ที่ประกอบด้วยรุ่นนี้
  2. มีแบบแผนการตั้งชื่อสำหรับไฟล์บันทึกการเปลี่ยนแปลงและตัวระบุชุดการเปลี่ยนแปลง และปฏิบัติตามนั้นแน่นอน
  3. หลีกเลี่ยงเซ็ตการเปลี่ยนแปลงที่มีการเปลี่ยนแปลงมากมาย ต้องการชุดการแก้ไขหลายชุดเป็นชุดการแก้ไขแบบยาวชุดเดียว
  4. หากคุณใช้กระบวนงานที่เก็บไว้และจำเป็นต้องอัปเดต ให้พิจารณาใช้ runOnChange="true" ของชุดการแก้ไขที่มีการเพิ่มขั้นตอนการจัดเก็บนั้น มิฉะนั้น ทุกครั้งที่มีการอัปเดต คุณจะต้องสร้างชุดการแก้ไขใหม่โดยใช้ขั้นตอนการจัดเก็บเวอร์ชันใหม่ ข้อกำหนดแตกต่างกันไป แต่การไม่ติดตามประวัติดังกล่าวมักเป็นที่ยอมรับได้
  5. พิจารณาการสควอชการเปลี่ยนแปลงที่ซ้ำซ้อนก่อนที่จะรวมสาขาของคุณลักษณะ บางครั้ง มันเกิดขึ้นที่สาขาคุณลักษณะ (โดยเฉพาะในกลุ่มที่มีอายุยืนยาว) ในภายหลัง ชุดการเปลี่ยนแปลงจะปรับแต่งการเปลี่ยนแปลงที่ทำในชุดการแก้ไขก่อนหน้า ตัวอย่างเช่น คุณอาจสร้างตารางแล้วตัดสินใจเพิ่มคอลัมน์ลงในตาราง ควรเพิ่มคอลัมน์เหล่านั้นในการเปลี่ยนแปลง createTable เริ่มต้น ถ้าสาขาคุณลักษณะนี้ยังไม่ได้รวมเข้ากับสาขาหลัก
  6. ใช้บันทึกการเปลี่ยนแปลงเดียวกันเพื่อสร้างฐานข้อมูลทดสอบ หากคุณพยายามทำเช่นนั้น คุณอาจพบว่าชุดการแก้ไขบางชุดไม่สามารถใช้กับสภาพแวดล้อมการทดสอบได้ หรือชุดการแก้ไขเพิ่มเติมจำเป็นสำหรับสภาพแวดล้อมการทดสอบเฉพาะนั้น ด้วย Liquibase ปัญหานี้แก้ไขได้อย่างง่ายดายโดยใช้ บริบท เพียงเพิ่มแอ็ตทริบิวต์ context="test" ให้กับชุดการเปลี่ยนแปลงที่จำเป็นต้องดำเนินการกับการทดสอบเท่านั้น จากนั้นจึงเริ่มต้น Liquibase โดยเปิดใช้งานบริบท test

ย้อนกลับ

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

 <changeSet author="..."> <createTable tableName="PRODUCT"> <column name="ID" type="BIGINT"> <constraints primaryKey="true" primaryKeyName="PK_PRODUCT"/> </column> <column name="CODE" type="VARCHAR(50)"> <constraints nullable="false" unique="true" uniqueConstraintName="UC_PRODUCT_CODE"/> </column> </createTable> <rollback> <dropTable tableName="PRODUCT"/> </rollback> </changeSet>

การย้อนกลับอย่างชัดเจนจะซ้ำซ้อนที่นี่ เนื่องจาก Liquibase จะดำเนินการย้อนกลับแบบเดียวกัน Liquibase สามารถย้อนกลับการเปลี่ยนแปลงส่วนใหญ่ที่รองรับได้โดยอัตโนมัติ เช่น addColumn createTable createIndex

แก้ไขอดีต

ไม่มีใครสมบูรณ์แบบ และเราทุกคนล้วนเคยทำผิดพลาด บางส่วนอาจถูกค้นพบสายเกินไปเมื่อการเปลี่ยนแปลงที่นิสัยเสียได้ถูกนำมาใช้แล้ว มาสำรวจกันว่าจะทำอะไรได้บ้างเพื่อช่วยวันนี้

อัปเดตฐานข้อมูลด้วยตนเอง

มันเกี่ยวข้องกับการยุ่งกับ DATABASECHANGELOG และฐานข้อมูลของคุณด้วยวิธีต่อไปนี้:

  1. หากคุณต้องการแก้ไขเซ็ตการแก้ไขที่ไม่ถูกต้องและดำเนินการอีกครั้ง:
    • ลบแถวออกจาก DATABASECHANGELOG ที่สอดคล้องกับชุดการแก้ไข
    • ลบผลข้างเคียงทั้งหมดที่เกิดจากเซ็ตการแก้ไข เช่น คืนค่าตารางถ้ามันถูกทิ้ง
    • แก้ไขเซ็ตการเปลี่ยนแปลงที่ไม่ดี
    • เรียกใช้การย้ายข้อมูลอีกครั้ง
  2. หากคุณต้องการแก้ไขเซ็ตการแก้ไขที่ไม่ถูกต้อง แต่ข้ามการใช้อีกครั้ง:
    • อัปเดต DATABASECHANGELOG โดยตั้งค่าฟิลด์ MD5SUM เป็น NULL สำหรับแถวเหล่านั้นที่สอดคล้องกับชุดการแก้ไขที่ไม่ถูกต้อง
    • แก้ไขสิ่งที่ผิดพลาดในฐานข้อมูลด้วยตนเอง ตัวอย่างเช่น หากมีคอลัมน์ที่เพิ่มด้วยประเภทที่ไม่ถูกต้อง ให้ทำการสอบถามเพื่อแก้ไขประเภท
    • แก้ไขเซ็ตการเปลี่ยนแปลงที่ไม่ดี
    • เรียกใช้การย้ายข้อมูลอีกครั้ง Liquibase จะคำนวณเช็คซัมใหม่และบันทึกลงใน MD5SUM ชุดการเปลี่ยนแปลงที่แก้ไขจะไม่ถูกเรียกใช้อีก

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

เขียนชุดการแก้ไขแก้ไข

ในทางปฏิบัติ แนวทางนี้มักจะเหมาะสมกว่า คุณอาจสงสัยว่า ทำไมไม่แก้ไขเซ็ตการแก้ไขเดิมล่ะ ความจริงก็คือมันขึ้นอยู่กับสิ่งที่จำเป็นต้องเปลี่ยน Liquibase คำนวณผลรวมการตรวจสอบสำหรับชุดการแก้ไขแต่ละชุด และปฏิเสธที่จะใช้การเปลี่ยนแปลงใหม่ หากผลรวมการตรวจสอบเป็นของใหม่สำหรับชุดการแก้ไขที่ใช้ก่อนหน้านี้อย่างน้อยหนึ่งชุด ลักษณะการทำงานนี้สามารถปรับแต่งตามชุดการเปลี่ยนแปลงโดยระบุ runOnChange="true" ผลรวมการตรวจสอบจะไม่ได้รับผลกระทบหากคุณแก้ไขเงื่อนไขเบื้องต้นหรือแอตทริบิวต์ชุดการเปลี่ยนแปลงที่เป็นตัวเลือก ( context , runOnChange เป็นต้น)

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

  1. หากคุณต้องการให้การเปลี่ยนแปลงเหล่านั้นยังคงใช้กับสคีมาใหม่ ให้เพิ่มเซ็ตการแก้ไขที่แก้ไข ตัวอย่างเช่น หากมีคอลัมน์ที่เพิ่มด้วยประเภทที่ไม่ถูกต้อง ให้แก้ไขประเภทของคอลัมน์ในชุดการแก้ไขใหม่
  2. หากคุณต้องการแสร้งทำเป็นว่าชุดการเปลี่ยนแปลงที่ไม่ดีเหล่านั้นไม่มีอยู่จริง ให้ทำดังต่อไปนี้:
    • ลบชุดการเปลี่ยนแปลงหรือเพิ่มแอ็ตทริบิวต์ context ด้วยค่าที่รับประกันว่าคุณจะไม่พยายามใช้การย้ายข้อมูลกับบริบทดังกล่าวอีก เช่น context="graveyard-changesets-never-run"
    • เพิ่มเซ็ตการแก้ไขใหม่ที่จะย้อนกลับสิ่งที่ทำผิดหรือแก้ไข การเปลี่ยนแปลงเหล่านี้ควรใช้เฉพาะเมื่อมีการเปลี่ยนแปลงที่ไม่ถูกต้อง สามารถทำได้โดยมีเงื่อนไขเบื้องต้น เช่น changeSetExecuted อย่าลืมเพิ่มความคิดเห็นที่อธิบายว่าเหตุใดคุณจึงทำเช่นนั้น
    • เพิ่มเซ็ตการแก้ไขใหม่ที่ปรับเปลี่ยนสคีมาอย่างถูกวิธี

อย่างที่คุณเห็น การแก้ไขอดีตเป็นไปได้ แม้ว่าอาจไม่ตรงไปตรงมาเสมอไป

บรรเทาความเจ็บปวดที่กำลังเติบโต

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

ภาพประกอบของบันทึกการเปลี่ยนแปลงที่ถูกบีบอัด

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

ภาพประกอบของฐานข้อมูลเมล็ดพันธุ์

บทสรุป

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