1. Liquibase 概述
1.1 Liquibase 是什么
Liquibase 是一个开源、与数据库无关的数据库变更管理工具。它通过声明式变更集(ChangeSet)和版本化变更日志(ChangeLog),将数据库 Schema 的每一次修改记录为可追踪、可回滚、可协作的“代码资产”。
核心工作模式:开发者编写数据库变更描述(XML / YAML / JSON / SQL),Liquibase 自动将其转换为目标数据库的 DDL/DML,并维护一张 DATABASECHANGELOG 表来记录哪些变更已执行、哪些尚未执行。
一句话定义:Liquibase = 数据库 Schema 的 Git + Maven,让数据库变更像应用代码一样受版本控制。
1.2 核心价值
| 价值维度 | 说明 | 典型收益 |
|---|---|---|
| 版本可追溯 | 每次变更生成唯一 ID,记录执行时间、作者、MD5 校验和 | 快速定位“谁、在何时、改了哪个表” |
| 环境一致性 | 开发、测试、预发、生产使用同一份 ChangeLog | 消除“在我机器上能跑”的 Schema 差异 |
| 自动回滚 | 支持 rollbackCount、rollbackToDate、rollbackToTag |
上线失败时秒级回退到安全版本 |
| 多数据库兼容 | 同一份 ChangeLog 可运行于 MySQL、PostgreSQL、Oracle、SQL Server 等 30+ 数据库 | 减少数据库迁移时的适配工作量 |
| CI/CD 集成 | 提供 Maven、Gradle、Spring Boot、命令行、Jenkins 等多种集成方式 | 数据库变更纳入自动化流水线 |
1.3 与传统 SQL 迁移的对比
| 对比维度 | 传统 SQL 迁移(手动脚本) | Liquibase 自动化迁移 |
|---|---|---|
| 变更记录 | 依赖文件夹命名或备注文档 | 自动写入 DATABASECHANGELOG 表 |
| 重复执行防护 | 需手动判断是否已执行(如 IF NOT EXISTS) |
基于 ChangeSet ID + 文件路径 + MD5 自动跳过已执行项 |
| 回滚能力 | 需单独编写回滚脚本,容易遗漏 | ChangeSet 内嵌 rollback 标签,自动生成回滚语句 |
| 数据库兼容 | 每套数据库维护独立脚本 | ChangeLog 抽象为数据库无关格式,自动转换 |
| 协作冲突 | 多人修改同一表时容易覆盖 | ChangeSet 按顺序执行,冲突在代码审查阶段暴露 |
| CI/CD 集成 | 需自行编写 Shell 或 Python 调度 | 原生支持 Maven/Gradle/Spring Boot 插件 |
注意:传统 SQL 迁移在小型项目或一次性数据修复中仍有价值,但在多环境、多版本、多人协作的企业级项目中,Liquibase 的自动化能力可显著降低人为失误风险。
1.4 适用场景
- 微服务架构:每个服务独立数据库,Liquibase 作为服务启动时的 Schema 初始化组件
- 持续交付流水线:数据库变更与应用代码一同构建、测试、部署
- 多环境管理:开发 → 测试 → 预发 → 生产,确保 Schema 版本一致
- 数据库即代码(Database as Code):将 DDL 纳入 Git 仓库,支持 Code Review
- 跨数据库迁移:从 MySQL 迁移到 PostgreSQL 或 Oracle 时,ChangeLog 可复用
- 合规审计:DATABASECHANGELOG 表提供完整的变更审计轨迹
1.5 Liquibase 核心工作流程
1.6 典型 ChangeLog 示例
<?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-4.20.xsd">
<changeSet id="1" author="zhangsan">
<createTable tableName="user">
<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)"/>
</createTable>
<rollback>
DROP TABLE IF EXISTS user;
</rollback>
</changeSet>
<changeSet id="2" author="lisi">
<addColumn tableName="user">
<column name="phone" type="varchar(20)"/>
</addColumn>
</changeSet>
</databaseChangeLog>
最佳实践:每个 ChangeSet 只包含一个逻辑变更(如创建一张表或添加一个字段),便于回滚和代码审查。ChangeSet 的 id 建议使用数字序列或 JIRA 工单号,避免使用时间戳(多人协作时容易冲突)。