Quick start: Chạy migration đầu tiên trong 5 phút
Cảnh tượng này hẳn rất quen thuộc: bạn gửi file patch_v2.sql qua Slack lúc nửa đêm, hồi hộp nhờ DevOps chạy tay trên production. Chỉ cần sót một lệnh hoặc lệch thứ tự file, toàn bộ ứng dụng lăn ra lỗi. Liquibase xử lý dứt điểm rủi ro này bằng cách biến schema database thành code, versioning chặt chẽ như Git.
Không cần cài đặt phức tạp. Bạn chỉ cần Docker và 2 file cấu hình để bắt đầu.
Đầu tiên, tạo file liquibase.properties để khai báo kết nối MySQL:
changeLogFile=changelog.xml
url=jdbc:mysql://localhost:3306/app_db?useSSL=false&allowPublicKeyRetrieval=true
username=root
password=rootpassword
driver=com.mysql.cj.jdbc.Driver
Tiếp theo, tạo file changelog.xml định nghĩa bảng users mẫu:
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="20241015-01" author="hoang-dev">
<createTable tableName="users">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="username" type="VARCHAR(50)">
<constraints nullable="false" unique="true"/>
</column>
<column name="email" type="VARCHAR(100)">
<constraints nullable="false"/>
</column>
<column name="created_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP"/>
</createTable>
</changeSet>
</databaseChangeLog>
Kích hoạt migration bằng lệnh Docker một dòng:
docker run --rm --network="host" \
-v $(pwd):/liquibase/changelog \
liquibase/liquibase \
--defaults-file=/liquibase/changelog/liquibase.properties \
update
Kiểm tra MySQL ngay sau khi lệnh chạy xong. Bên cạnh bảng users, bạn sẽ thấy 2 bảng hệ thống mới xuất hiện: DATABASECHANGELOG và DATABASECHANGELOGLOCK. Đây chính là trái tim của Liquibase.
Bên dưới nắp capo: Liquibase vận hành ra sao?
File SQL thuần không thể tự nhớ trạng thái. Khi dự án có 15 developer cùng sửa schema trên hàng chục microservice, bạn không thể biết bảng trên Staging đã khớp với Production hay chưa.
Liquibase kiểm soát toàn bộ chu trình này thông qua hai thành phần cốt lõi.
1. ChangeSet và ChangeLog
Mỗi thay đổi dữ liệu được gói gọn trong một ChangeSet. Tập hợp các ChangeSet nằm chung trong file ChangeLog.
- id: Mã định danh thay đổi. Nên đặt theo format ngày tháng hoặc mã Jira ticket (ví dụ:
PROJ-102-add-phone-col). - author: Tên người tạo thay đổi, phục vụ audit trail khi điều tra log.
- Khóa định danh: Bộ ba
id + author + filepathtạo thành unique key. Liquibase dựa vào bộ ba này để quyết định có chạy script hay bỏ qua.
2. Quy trình xử lý với 2 bảng hệ thống
Khi bạn gõ update, Liquibase thực thi 4 bước tuần tự:
- Kiểm tra khóa: Đọc bảng
DATABASECHANGELOGLOCK. CộtLOCKEDđược set thành1. Cơ chế này chặn xung đột khi 2 pipeline CI/CD chạy cùng lúc. - Đối soát Checksum: Quét bảng
DATABASECHANGELOG, đọc mã MD5 hash của từng ChangeSet để phát hiện những script đã chạy. - Thực thi: Áp dụng các ChangeSet mới vào MySQL. Ghi nhận ngay một dòng audit log kèm thời gian thực thi (execution time tính bằng millisecond).
- Mở khóa: Đặt lại
LOCKED = 0trongDATABASECHANGELOGLOCKđể nhường quyền cho tiến trình tiếp theo.
3. Rollback an toàn khi có sự cố
Deploy thất bại giữa đêm là rủi ro luôn chực chờ. Khối <rollback> cho phép bạn trả database về trạng thái cũ chỉ với một lệnh rollback-count 1:
<changeSet id="20241015-02" author="hoang-dev">
<addColumn tableName="users">
<column name="phone_number" type="VARCHAR(20)"/>
</addColumn>
<rollback>
<dropColumn tableName="users" columnName="phone_number"/>
</rollback>
</changeSet>
Tích hợp Liquibase vào pipeline GitHub Actions
Đừng bao giờ để developer chạy lệnh migration từ máy cá nhân lên server production. Toàn bộ thao tác phải được cô lập trong pipeline CI/CD.
Dưới đây là workflow GitHub Actions chuẩn mẫu:
name: Database Migration
on:
push:
branches:
- main
paths:
- 'db/changelog/**'
jobs:
migrate:
runs-on: ubuntu-latest
steps:
- name: Checkout Source Code
uses: actions/checkout@v4
- name: Liquibase Update
uses: liquibase-github-actions/[email protected]
with:
changelogFile: 'db/changelog/changelog.xml'
url: ${{ secrets.DB_JDBC_URL }}
username: ${{ secrets.DB_USER }}
password: ${{ secrets.DB_PASSWORD }}
headless: true
Áp dụng quy trình 3 chặng để đảm bảo an toàn tuyệt đối:
- Chặng 1 (Dry-run & Review): Chạy lệnh
update-sqlđể trích xuất raw SQL ra artifact. DBA có thể review chính xác câu lệnh sắp chạy trước khi chạm vào dữ liệu thật. - Chặng 2 (Staging Migration): Tự động migrate trên môi trường Staging với dữ liệu tương đương Production để phát hiện sớm lỗi timeout hoặc cú pháp.
- Chặng 3 (Production Approval): Cài đặt Manual Approval Gate trên GitHub Actions. Chỉ khi Tech Lead bấm duyệt, job mới kết nối vào database chính thức.
3 bài học xương máu khi chạy Liquibase trên MySQL
Trên cụm MySQL 8.0 chứa 40 triệu bản ghi (khoảng 85GB data), một lệnh DDL bất cẩn có thể làm tê liệt toàn bộ hệ thống bán hàng. Hãy ghi nhớ các nguyên tắc sau:
- Coi chừng Metadata Lock trên bảng lớn: Dù MySQL 8.0 hỗ trợ
ALGORITHM=INSTANTcho một số thao tác thêm cột, các lệnh đổi kiểu dữ liệu hay đánh index vẫn yêu cầu Exclusive Lock. Với bảng trên 5 triệu dòng, hãy bọc công cụ online schema change nhưgh-osthoặcpt-online-schema-changevào thẻ<customChange>thay vì gọi DDL trực tiếp. - Tuyệt đối không sửa ChangeSet đã chạy: Liquibase kiểm tra tính toàn vẹn bằng MD5 checksum. Nếu bạn sửa file đã apply trên production, pipeline sẽ văng lỗi
ValidationFailedException: MD5Sum check failedvà dừng ngay lập tức. Muốn sửa logic, hãy tạo ChangeSet mới với ID tiếp theo. - Phòng ngừa lỗi trùng lặp bằng Preconditions: Dùng điều kiện kiểm tra trước khi chạy. Thẻ
<preConditions>giúp script chạy lại an toàn mà không quăng lỗi bảng hoặc index đã tồn tại:
<changeSet id="20241015-03" author="hoang-dev">
<preConditions onFail="MARK_RAN">
<not>
<indexExists indexName="idx_users_email" tableName="users"/>
</not>
</preConditions>
<createIndex indexName="idx_users_email" tableName="users">
<column name="email"/>
</createIndex>
</changeSet>
Tự động hóa schema migration không chỉ giải phóng lập trình viên khỏi những đêm trực deploy căng thẳng. Quan trọng hơn, nó biến database thành một phần có thể kiểm thử, theo dõi và phục hồi trong chu trình DevOps hiện đại.

