การย้ายฐานข้อมูล: เปลี่ยนหนอนผีเสื้อให้เป็นผีเสื้อ
เผยแพร่แล้ว: 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>
ประสิทธิภาพจะลดลงหากคุณมีผลิตภัณฑ์หลายล้านชิ้น เพื่อให้การย้ายข้อมูลเร็วขึ้น เราสามารถเขียนใหม่เป็นขั้นตอนต่อไปนี้:
- สร้างตารางใหม่สำหรับผลิตภัณฑ์ด้วย
createTable
เช่นเดียวกับที่เราเห็นก่อนหน้านี้ ในขั้นตอนนี้ เป็นการดีกว่าที่จะสร้างข้อจำกัดให้น้อยที่สุด มาตั้งชื่อตารางใหม่กันPRODUCT_TMP
- เติม
PRODUCT_TMP
ด้วย SQL ในรูปแบบของINSERT INTO ... SELECT ...
โดยใช้sql
change - สร้างข้อจำกัดทั้งหมด (
addNotNullConstraint
,addUniqueConstraint
,addForeignKeyConstraint
) และดัชนี (createIndex
) ที่คุณต้องการ - เปลี่ยนชื่อตาราง
PRODUCT
เป็นบางอย่างเช่นPRODUCT_BAK
Liquibase สามารถทำได้ด้วยrenameTable
- เปลี่ยนชื่อ
PRODUCT_TMP
เป็นPRODUCT
(อีกครั้งโดยใช้renameTable
) - ทางเลือก ลบ
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
หลักการตั้งชื่อนั้นขัดแย้งกันมาก เราจึงไม่คิดว่าจะให้คำแนะนำอย่างครอบคลุมในที่นี้ มีความสม่ำเสมอ เคารพข้อตกลงในทีมหรือโครงการของคุณ หรือเพียงแค่ประดิษฐ์คิดค้นขึ้นมาหากไม่มี
การจัดระเบียบชุดการเปลี่ยนแปลง
สิ่งแรกที่ต้องตัดสินใจคือจะเก็บเซ็ตการแก้ไขไว้ที่ไหน โดยทั่วไปมีสองวิธี:
- เก็บชุดการเปลี่ยนแปลงด้วยรหัสแอปพลิเคชัน การทำเช่นนี้สะดวกเพราะคุณสามารถคอมมิตและตรวจสอบชุดการเปลี่ยนแปลงและโค้ดแอปพลิเคชันร่วมกันได้
- เก็บชุดการเปลี่ยนแปลงและรหัสแอปพลิเคชันแยกจากกัน เช่น ในที่เก็บ VCS แยกต่างหาก แนวทางนี้เหมาะสมเมื่อมีการแชร์โมเดลข้อมูลในแอปพลิเคชันต่างๆ และสะดวกกว่าในการจัดเก็บเซ็ตการแก้ไขทั้งหมดในที่เก็บเฉพาะ และไม่กระจายไปตามที่เก็บหลายแห่งที่มีโค้ดของแอปพลิเคชันอยู่
เมื่อใดก็ตามที่คุณจัดเก็บชุดการแก้ไข โดยทั่วไปแล้วจะเหมาะสมที่จะแบ่งชุดการแก้ไขออกเป็นหมวดหมู่ต่อไปนี้:
- การโยกย้ายอิสระที่ไม่ส่งผลต่อระบบที่ทำงานอยู่ โดยปกติแล้ว การสร้างตาราง ลำดับ ฯลฯ ใหม่จะปลอดภัย หากแอปพลิเคชันที่ปรับใช้ในปัจจุบันยังไม่ทราบ
- การปรับเปลี่ยนสคีมาที่เปลี่ยนโครงสร้างของร้านค้า เช่น การเพิ่มหรือวางคอลัมน์และดัชนี ไม่ควรนำการเปลี่ยนแปลงเหล่านี้ไปใช้ในขณะที่แอปพลิเคชันรุ่นเก่ากว่ายังคงใช้งานอยู่ เนื่องจากอาจนำไปสู่การล็อกหรือการทำงานที่แปลกประหลาดเนื่องจากการเปลี่ยนแปลงในสคีมา
- การโยกย้ายอย่างรวดเร็วที่แทรกหรืออัปเดตข้อมูลจำนวนเล็กน้อย หากมีการใช้งานหลายแอพพลิเคชั่น ชุดการเปลี่ยนแปลงจากหมวดหมู่นี้สามารถดำเนินการได้พร้อมกันโดยไม่ทำให้ประสิทธิภาพของฐานข้อมูลลดลง
- การย้ายข้อมูลอาจช้าซึ่งแทรกหรืออัปเดตข้อมูลจำนวนมาก การเปลี่ยนแปลงเหล่านี้จะดีกว่าที่จะใช้เมื่อไม่มีการโยกย้ายที่คล้ายกันอื่น ๆ

ชุดการย้ายข้อมูลเหล่านี้ควรรันอย่างต่อเนื่องก่อนที่จะปรับใช้แอปพลิเคชันเวอร์ชันใหม่กว่า แนวทางนี้จะยิ่งนำไปใช้ได้จริงมากขึ้นหากระบบประกอบด้วยแอพพลิเคชั่นที่แยกจากกันหลายแอพพลิเคชั่น และบางแอพพลิเคชั่นก็ใช้ฐานข้อมูลเดียวกัน มิฉะนั้น การแยกเฉพาะเซ็ตการแก้ไขที่สามารถใช้ได้โดยไม่กระทบต่อแอปพลิเคชันที่ทำงานอยู่ นับว่าคุ้มค่า และชุดการแก้ไขที่เหลืออาจใช้ร่วมกันได้
สำหรับแอปพลิเคชันที่ง่ายกว่า สามารถใช้ชุดการย้ายข้อมูลที่จำเป็นทั้งหมดเมื่อเริ่มต้นแอปพลิเคชัน ในกรณีนี้ ชุดการเปลี่ยนแปลงทั้งหมดจะจัดเป็นหมวดหมู่เดียวและจะทำงานทุกครั้งที่มีการเริ่มต้นแอปพลิเคชัน
ไม่ว่าจะเลือกขั้นตอนใดเพื่อใช้การย้ายข้อมูลก็ตาม ควรสังเกตว่าการใช้ฐานข้อมูลเดียวกันสำหรับหลายแอปพลิเคชันอาจทำให้เกิดการล็อกเมื่อมีการใช้การย้ายข้อมูล Liquibase (เช่นเดียวกับโซลูชันอื่นๆ ที่คล้ายกัน) ใช้ตารางพิเศษสองตารางเพื่อบันทึกข้อมูลเมตา: DATABASECHANGELOG
และ DATABASECHANGELOGLOCK
อันแรกใช้สำหรับเก็บข้อมูลเกี่ยวกับเซ็ตการแก้ไขที่ใช้ และอันหลังเพื่อป้องกันการโยกย้ายพร้อมกันภายในสคีมาฐานข้อมูลเดียวกัน ดังนั้น หากหลายแอปพลิเคชันต้องใช้สกีมาฐานข้อมูลเดียวกันด้วยเหตุผลบางประการ ควรใช้ชื่อที่ไม่ใช่ค่าเริ่มต้นสำหรับตารางข้อมูลเมตาเพื่อหลีกเลี่ยงการล็อก
เมื่อโครงสร้างระดับสูงมีความชัดเจนแล้ว คุณต้องตัดสินใจว่าจะจัดระเบียบชุดการเปลี่ยนแปลงภายในแต่ละหมวดหมู่อย่างไร
ขึ้นอยู่กับข้อกำหนดการใช้งานเฉพาะอย่างมาก แต่ประเด็นต่อไปนี้มักจะสมเหตุสมผล:
- จัดกลุ่มบันทึกการเปลี่ยนแปลงตามการเปิดตัวผลิตภัณฑ์ของคุณ สร้างไดเร็กทอรีใหม่สำหรับแต่ละรีลีส และวางไฟล์บันทึกการเปลี่ยนแปลงที่เกี่ยวข้องลงในไดเร็กทอรี มีบันทึกการเปลี่ยนแปลงรูทและรวมบันทึกการเปลี่ยนแปลงที่สอดคล้องกับรุ่นต่างๆ ในบันทึกการเปลี่ยนแปลงการเผยแพร่ ให้รวมบันทึกการเปลี่ยนแปลงอื่นๆ ที่ประกอบด้วยรุ่นนี้
- มีแบบแผนการตั้งชื่อสำหรับไฟล์บันทึกการเปลี่ยนแปลงและตัวระบุชุดการเปลี่ยนแปลง และปฏิบัติตามนั้นแน่นอน
- หลีกเลี่ยงเซ็ตการเปลี่ยนแปลงที่มีการเปลี่ยนแปลงมากมาย ต้องการชุดการแก้ไขหลายชุดเป็นชุดการแก้ไขแบบยาวชุดเดียว
- หากคุณใช้กระบวนงานที่เก็บไว้และจำเป็นต้องอัปเดต ให้พิจารณาใช้
runOnChange="true"
ของชุดการแก้ไขที่มีการเพิ่มขั้นตอนการจัดเก็บนั้น มิฉะนั้น ทุกครั้งที่มีการอัปเดต คุณจะต้องสร้างชุดการแก้ไขใหม่โดยใช้ขั้นตอนการจัดเก็บเวอร์ชันใหม่ ข้อกำหนดแตกต่างกันไป แต่การไม่ติดตามประวัติดังกล่าวมักเป็นที่ยอมรับได้ - พิจารณาการสควอชการเปลี่ยนแปลงที่ซ้ำซ้อนก่อนที่จะรวมสาขาของคุณลักษณะ บางครั้ง มันเกิดขึ้นที่สาขาคุณลักษณะ (โดยเฉพาะในกลุ่มที่มีอายุยืนยาว) ในภายหลัง ชุดการเปลี่ยนแปลงจะปรับแต่งการเปลี่ยนแปลงที่ทำในชุดการแก้ไขก่อนหน้า ตัวอย่างเช่น คุณอาจสร้างตารางแล้วตัดสินใจเพิ่มคอลัมน์ลงในตาราง ควรเพิ่มคอลัมน์เหล่านั้นในการเปลี่ยนแปลง
createTable
เริ่มต้น ถ้าสาขาคุณลักษณะนี้ยังไม่ได้รวมเข้ากับสาขาหลัก - ใช้บันทึกการเปลี่ยนแปลงเดียวกันเพื่อสร้างฐานข้อมูลทดสอบ หากคุณพยายามทำเช่นนั้น คุณอาจพบว่าชุดการแก้ไขบางชุดไม่สามารถใช้กับสภาพแวดล้อมการทดสอบได้ หรือชุดการแก้ไขเพิ่มเติมจำเป็นสำหรับสภาพแวดล้อมการทดสอบเฉพาะนั้น ด้วย 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
และฐานข้อมูลของคุณด้วยวิธีต่อไปนี้:
- หากคุณต้องการแก้ไขเซ็ตการแก้ไขที่ไม่ถูกต้องและดำเนินการอีกครั้ง:
- ลบแถวออกจาก
DATABASECHANGELOG
ที่สอดคล้องกับชุดการแก้ไข - ลบผลข้างเคียงทั้งหมดที่เกิดจากเซ็ตการแก้ไข เช่น คืนค่าตารางถ้ามันถูกทิ้ง
- แก้ไขเซ็ตการเปลี่ยนแปลงที่ไม่ดี
- เรียกใช้การย้ายข้อมูลอีกครั้ง
- ลบแถวออกจาก
- หากคุณต้องการแก้ไขเซ็ตการแก้ไขที่ไม่ถูกต้อง แต่ข้ามการใช้อีกครั้ง:
- อัปเดต
DATABASECHANGELOG
โดยตั้งค่าฟิลด์MD5SUM
เป็นNULL
สำหรับแถวเหล่านั้นที่สอดคล้องกับชุดการแก้ไขที่ไม่ถูกต้อง - แก้ไขสิ่งที่ผิดพลาดในฐานข้อมูลด้วยตนเอง ตัวอย่างเช่น หากมีคอลัมน์ที่เพิ่มด้วยประเภทที่ไม่ถูกต้อง ให้ทำการสอบถามเพื่อแก้ไขประเภท
- แก้ไขเซ็ตการเปลี่ยนแปลงที่ไม่ดี
- เรียกใช้การย้ายข้อมูลอีกครั้ง Liquibase จะคำนวณเช็คซัมใหม่และบันทึกลงใน
MD5SUM
ชุดการเปลี่ยนแปลงที่แก้ไขจะไม่ถูกเรียกใช้อีก
- อัปเดต
เห็นได้ชัดว่า การทำเคล็ดลับเหล่านี้ทำได้ง่ายในระหว่างการพัฒนา แต่จะยากขึ้นมากหากนำการเปลี่ยนแปลงไปใช้กับหลายฐานข้อมูล
เขียนชุดการแก้ไขแก้ไข
ในทางปฏิบัติ แนวทางนี้มักจะเหมาะสมกว่า คุณอาจสงสัยว่า ทำไมไม่แก้ไขเซ็ตการแก้ไขเดิมล่ะ ความจริงก็คือมันขึ้นอยู่กับสิ่งที่จำเป็นต้องเปลี่ยน Liquibase คำนวณผลรวมการตรวจสอบสำหรับชุดการแก้ไขแต่ละชุด และปฏิเสธที่จะใช้การเปลี่ยนแปลงใหม่ หากผลรวมการตรวจสอบเป็นของใหม่สำหรับชุดการแก้ไขที่ใช้ก่อนหน้านี้อย่างน้อยหนึ่งชุด ลักษณะการทำงานนี้สามารถปรับแต่งตามชุดการเปลี่ยนแปลงโดยระบุ runOnChange="true"
ผลรวมการตรวจสอบจะไม่ได้รับผลกระทบหากคุณแก้ไขเงื่อนไขเบื้องต้นหรือแอตทริบิวต์ชุดการเปลี่ยนแปลงที่เป็นตัวเลือก ( context
, runOnChange
เป็นต้น)
ตอนนี้ คุณอาจสงสัยว่าในที่สุดคุณจะแก้ไขชุดการเปลี่ยนแปลงที่มีข้อผิดพลาดได้อย่างไร
- หากคุณต้องการให้การเปลี่ยนแปลงเหล่านั้นยังคงใช้กับสคีมาใหม่ ให้เพิ่มเซ็ตการแก้ไขที่แก้ไข ตัวอย่างเช่น หากมีคอลัมน์ที่เพิ่มด้วยประเภทที่ไม่ถูกต้อง ให้แก้ไขประเภทของคอลัมน์ในชุดการแก้ไขใหม่
- หากคุณต้องการแสร้งทำเป็นว่าชุดการเปลี่ยนแปลงที่ไม่ดีเหล่านั้นไม่มีอยู่จริง ให้ทำดังต่อไปนี้:
- ลบชุดการเปลี่ยนแปลงหรือเพิ่มแอ็ตทริบิวต์
context
ด้วยค่าที่รับประกันว่าคุณจะไม่พยายามใช้การย้ายข้อมูลกับบริบทดังกล่าวอีก เช่นcontext="graveyard-changesets-never-run"
- เพิ่มเซ็ตการแก้ไขใหม่ที่จะย้อนกลับสิ่งที่ทำผิดหรือแก้ไข การเปลี่ยนแปลงเหล่านี้ควรใช้เฉพาะเมื่อมีการเปลี่ยนแปลงที่ไม่ถูกต้อง สามารถทำได้โดยมีเงื่อนไขเบื้องต้น เช่น
changeSetExecuted
อย่าลืมเพิ่มความคิดเห็นที่อธิบายว่าเหตุใดคุณจึงทำเช่นนั้น - เพิ่มเซ็ตการแก้ไขใหม่ที่ปรับเปลี่ยนสคีมาอย่างถูกวิธี
- ลบชุดการเปลี่ยนแปลงหรือเพิ่มแอ็ตทริบิวต์
อย่างที่คุณเห็น การแก้ไขอดีตเป็นไปได้ แม้ว่าอาจไม่ตรงไปตรงมาเสมอไป
บรรเทาความเจ็บปวดที่กำลังเติบโต
เมื่อแอปพลิเคชันของคุณมีอายุมากขึ้น บันทึกการเปลี่ยนแปลงของแอปพลิเคชันก็จะเพิ่มขึ้นเช่นกัน โดยจะรวบรวมการเปลี่ยนแปลงของสคีมาทุกรายการตลอดเส้นทาง เกิดจากการออกแบบ และไม่มีอะไรผิดปกติในเรื่องนี้ บันทึกการเปลี่ยนแปลงแบบยาวสามารถทำให้สั้นลงได้โดยการบีบการโยกย้ายอย่างสม่ำเสมอ เช่น หลังจากปล่อยผลิตภัณฑ์แต่ละเวอร์ชัน ในบางกรณี จะทำให้การเริ่มต้นสคีมาใหม่เร็วขึ้น
การสควอชไม่ใช่เรื่องเล็กน้อยเสมอไป และอาจทำให้เกิดการถดถอยโดยไม่ก่อให้เกิดประโยชน์มากมาย ทางเลือกที่ดีอีกทางหนึ่งคือการใช้ฐานข้อมูลตั้งต้นเพื่อหลีกเลี่ยงการดำเนินการเซ็ตการแก้ไขทั้งหมด เหมาะอย่างยิ่งสำหรับการทดสอบสภาพแวดล้อม หากคุณต้องการเตรียมฐานข้อมูลให้พร้อมโดยเร็วที่สุด แม้กระทั่งกับข้อมูลทดสอบบางอย่าง คุณอาจคิดว่ามันเป็นรูปแบบหนึ่งของการบีบสำหรับเซ็ตการแก้ไข: ในบางจุด (เช่น หลังจากปล่อยเวอร์ชันอื่น) คุณทำการดัมพ์ของสคีมา หลังจากกู้คืนการถ่ายโอนข้อมูล คุณจะใช้การย้ายข้อมูลตามปกติ เฉพาะการเปลี่ยนแปลงใหม่เท่านั้นที่จะถูกนำไปใช้ เนื่องจากมีการนำการเปลี่ยนแปลงที่เก่ากว่าไปใช้ก่อนที่จะทำการดัมพ์ ดังนั้นพวกเขาจึงได้รับการฟื้นฟูจากที่ทิ้งขยะ
บทสรุป
เราจงใจหลีกเลี่ยงการเจาะลึกในคุณสมบัติของ Liquibase เพื่อนำเสนอบทความที่สั้นและตรงประเด็น โดยเน้นที่การพัฒนาสคีมาโดยทั่วไป หวังว่าจะเป็นที่ชัดเจนว่าประโยชน์และปัญหาที่เกิดขึ้นจากการใช้การย้ายสคีมาฐานข้อมูลแบบอัตโนมัตินั้นมีประโยชน์และปัญหาเพียงใด และทั้งหมดนี้เข้ากับวัฒนธรรม DevOps ได้ดีเพียงใด สิ่งสำคัญคือต้องไม่เปลี่ยนแม้แต่ความคิดดีๆ ให้กลายเป็นความเชื่อ ข้อกำหนดแตกต่างกันไป และในฐานะวิศวกรฐานข้อมูล การตัดสินใจของเราควรส่งเสริมการขับเคลื่อนผลิตภัณฑ์ไปข้างหน้า ไม่ใช่แค่การปฏิบัติตามคำแนะนำจากใครบางคนบนอินเทอร์เน็ต