文档地址
https://docs.liquibase.com/home.html
数据库更改管理:状态和迁移方法
有两种方法来管理数据库更改。第一种方法是基于状态的(或声明性的)的 , 其中定义了数据库的所需状态。可以通过对应工具将目标环境与定义的所需状态进行比对(或对比),用于生成允许目标环境与声明状态匹配的迁移脚本。另一种方法是基于迁移(或命令的),其中描述了用于更改数据库状态的特定迁移。通过对应工具能够显式跟踪和排序各个迁移,并将尚未部署到目标环境的迁移正确迁移到目标数据库。
虽然Liquibase
能够进行比对(或对比),但它从根本上说是基于迁移的解决方案。Liquibase
的比对能力仅用于协助加入新项目,并检查数据库迁移是否得到正确应用。作为基于迁移的解决方案,Liquibase
可以轻松:
Liquibase
的核心是依靠一种简单的机制来跟踪、版本和部署更改:
Liquibase
使用更改日志(是更改的分类)按特定顺序显式列出数据库更改。更改日志中的每个更改都是一个change set
。更改日志可以任意嵌套,以帮助组织和管理数据库迁移。
change set
都尽可能原子性更改,以避免失败的结果使数据库中剩下的未处理的语句处于unknown
状态;不过,可以将大型 SQL 脚本视为单个更改集。Liquibase
使用跟踪表(具体称为DATABASECHANGELOG
),该表位于每个数据库上,并跟踪已部署更改日志中的change set
。
Liquibase
所在的数据库没有跟踪表,Liquibase
将创建一个跟踪表。Liquibase
具有生成一条更改日志以表示数据库模式当前状态的功能。使用分类和跟踪表,Liquibase
能够:
Liquibase
只能将以前尚未部署到数据库的更改部署到数据库中。
Liquibase
具有上下文、标签和先决条件等高级功能,可精确控制changeSet的部署时间以及位置。数据库迁移可以在 SQL
、XML
、JSON
或 YAML
中定义.
1. 下载和解压Liquibase
2. 配置jdk环境
3. 下载要连接的数据库驱动
4. 创建一个liquibase.properties文件
示例:
driver: com.mysql.jdbc.Driver
classpath: ./mysql-connector-java-5.1.38.jar
url: jdbc:mysql://localhost:3306/tes2?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
username: root
password: root
changeLogFile: myChangeLog.xml
5. 创建一个建立一个Change Log
这是一次性步骤,用于配置更改日志以指向将包含SQL脚本。在解压的*.zip 或*.tar.gz的 Liquibase 的目录中创建并保存文件名为myChangeLog.xml
的文件。如下所示:
<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-3.8.xsd">
databaseChangeLog>
1. 在Change Log中直接添加sql脚本也可使用includeAll标签包含其他changeSet
<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-3.8.xsd">
<includeAll path="changelog/" relativeToChangelogFile="false"/>
databaseChangeLog>
2. 添加changeSet
changelog-2022032901.xml
<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-3.8.xsd">
<changeSet id="20220329-01" author="solo">
<createTable tableName="dh_project_category" remarks="项目类型表">
<column name="id" type="varchar(64)" remarks="项目类型id">
<constraints primaryKey="true" nullable="false"/>
column>
<column name="name" type="varchar(255)" remarks="类目类型名称"/>
<column name="status" type="int(11)" remarks="状态。1正常,2删除"/>
<column name="remark" type="varchar(255)" remarks="备注"/>
createTable>
changeSet>
databaseChangeLog>
changelog-2022032902.xml
<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-3.8.xsd">
<changeSet id="20220329-02" author="solo">
<sqlFile path="sql/b2.sql">sqlFile>
changeSet>
databaseChangeLog>
3. 添加b2.sql
CREATE TABLE `b2` (
`id` int(255) NOT NULL COMMENT '用户号码',
`lac` int(11) DEFAULT NULL COMMENT '区域',
`ci` int(11) DEFAULT NULL COMMENT '小区',
`upflow` int(11) DEFAULT NULL COMMENT '流量',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4. 执行命令
linux运行
./liquibase updatewindows运行
./liquibase.bat update5. 查看结果
1. 引入maven依赖
<dependency>
<groupId>org.liquibasegroupId>
<artifactId>liquibase-coreartifactId>
<version>${last.version}version>
dependency>
2. 设置LiquibaseConfig
@Configuration
public class LiquibaseConfig {
@Bean
public SpringLiquibase liquibase(DataSource dataSource) {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource);
//指定changelog的位置,这里使用的一个master文件引用其他文件的方式
liquibase.setChangeLog("classpath:liquibase/myChangeLog.xml");
liquibase.setShouldRun(true);
return liquibase;
}
}
3. changelog
myChangeLog.xml
<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-3.8.xsd">
<includeAll path="liquibase/changelog/" relativeToChangelogFile="false"/>
databaseChangeLog>
其余文件配置(和命令行相同)
这样在项目启动就会执行Liquibase的脚本
除了配置信息在yml中配置外,其余和普通maven项目集成的步骤相同
yml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/tes2?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
username: root
password: root
liquibase:
enabled: true
change-log: classpath:liquibase/myChangeLog.xml
1. 引入maven插件依赖
<plugin>
<groupId>org.liquibasegroupId>
<artifactId>liquibase-maven-pluginartifactId>
<version>${last.version}version>
<configuration>
<propertyFile>src/main/resources/liquibase/liquibase.propertiespropertyFile>
configuration>
<executions>
<execution>
<phase>process-resourcesphase>
<goals>
<goal>updategoal>
goals>
execution>
executions>
plugin>
2. 添加liquibase.properties文件
添加liquibase.properties文件,其余文件则和命令行的配置相同
3. 执行命令
//执行脚本
mvn liquibase:update
//标记标签
mvn liquibase:tag '-Dliquibase.tag=v1'
//回滚
mvn liquibase:rollbackSQL '-Dliquibase.rollbackTag=v1.0'
...
执行 ./liquibase rollbackcount 1 会发现b2表已被删除,databasechangelog表中对应的数据也被删除。
changelog-2022032902.xml
<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-3.8.xsd">
<changeSet id="20220329-02" author="solo">
<sqlFile path="sql/b2.sql">sqlFile>
<rollback>
DROP TABLE b2;
rollback>
changeSet>
<changeSet id="20220329-02-v1" author="solo">
<tagDatabase tag="v1"/>
changeSet>
databaseChangeLog>
changelog-2022032903.xml
<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-3.8.xsd">
<changeSet id="20220329-03" author="solo">
<sqlFile path="sql/b3.sql">sqlFile>
<rollback>
DROP TABLE b3;
rollback>
changeSet>
<changeSet id="20220329-03-v2" author="solo">
<tagDatabase tag="v2"/>
changeSet>
databaseChangeLog>
执行命令 ./liquibase rollback v1,会回滚到有v1标签的changlog状态,例子中也就是会回到dh_project_category和b2表,b3表会被回滚
当您想要捕获数据库的当前状态,然后将这些更改应用到任意数量的数据库时,通常会使用该命令
执行命令
./liquibase --changeLogFile=dbchangelog.mysql.sql generateChangeLog
或者
./liquibase --changeLogFile=dbchangelog.xml generateChangeLog
执行update命令将生成的变更日志还原到目标库中
./liquibase --changeLogFile=dbchangelog.xml update
或者
./liquibase --changeLogFile=dbchangelog.mysql.sql update
注意:当使用update命令应用changelog时,Liquibase不会创建新的数据库或模式。您必须将更改日志应用到它之前创建它们。
依旧按之前记录的前置准备工作来执行,然后修改liquibase.properties
# 指定目录和 jar 文件以搜索更改日志文件和自定义扩展类
classpath: ./mysql-connector-java-5.1.38.jar
# 指定目标数据库的驱动程序类名称
driver: com.mysql.jdbc.Driver
# 指定要用于与源数据库进行比较的数据库。也称为您的目标
url: jdbc:mysql://localhost:3306/tes3?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
# 指定目标数据库的用户名
username: root
# 指定目标数据库的密码
password: root
# 指定源数据库的驱动程序类名称
referenceDriver: com.mysql.jdbc.Driver
# 指定执行比较的源数据库
referenceUrl: jdbc:mysql://localhost:3306/tes2?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
# 指定源数据库的用户名
referenceUsername: root
# 指定源数据库的密码
referencePassword: root
# 指定要执行的更改日志的路径
changeLogFile: myChangeLog.xml
在测试目录下打开命令行,执行以下命令
./liquibase dbDoc D:/liquibase/doc --logLevel=error
命令执行完成后,会生成.html文件,用浏览器打开即可逐层查看数据库结构
在测试目录下打开命令行,执行以下命令
./liquibase --changeLogFile="changeLogFiledevtest.mysql.sql" diffChangeLog
生成的脚本如下
-- liquibase formatted sql
CREATE TABLE dh_project_category (
id VARCHAR(64) NOT NULL,
name VARCHAR(255) NULL COMMENT '类目类型名称',
status INT NULL COMMENT '状态。1正常,2删除',
remark VARCHAR(255) NULL COMMENT '备注',
CONSTRAINT PK_DH_PROJECT_CATEGORY PRIMARY KEY (id)) COMMENT='项目类型表';
ALTER TABLE dh_project_category COMMENT = '项目类型表';
ALTER TABLE dh_project_category MODIFY COLUMN id VARCHAR(64) COMMENT '项目类型id';
ALTER TABLE dh_project_category MODIFY COLUMN name VARCHAR(255) COMMENT '类目类型名称';
ALTER TABLE dh_project_category MODIFY COLUMN status INT COMMENT '状态。1正常,2删除';
ALTER TABLE dh_project_category MODIFY COLUMN remark VARCHAR(255) COMMENT '备注';
使用差异更新数据库
liquibase --changeLogFile="changeLogFiledevtest.mysql.sql" update
该history命令列出所有已部署的变更集及其deploymentIds. 该history命令不修改数据库
运行命令
./liquibase --outputFile=history.txt history
结果
Liquibase History for jdbc:mysql://localhost:3306/tes3?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
- Database updated at 22-3-31 ÏÂÎç2:23. Applied 3 changeset(s) in 0.0s, DeploymentId: 8707831594
dbchangelog.xml::1648706742235-1::lukuan (generated)
dbchangelog.xml::1648706742235-2::lukuan (generated)
dbchangelog.xml::1648706742235-3::lukuan (generated)
该status --verbose命令列出所有未部署的变更集。它还列出了每个未部署的变更集的 id、作者和文件路径名。该status --verbose命令不修改数据库
运行命令
./liquibase --changeLogFile=myChangeLog.xml status --verbose
结果
Starting Liquibase at 16:30:17 (version 4.9.0 #1885 built at 2022-03-16 16:58+0000)
Liquibase Version: 4.9.0
Liquibase Community 4.9.0 by Liquibase
2 change sets have not been applied to root@localhost@jdbc:mysql://localhost:3306/tes3?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
changelog/changelog-2022032903.xml::20220329-03::solo
changelog/changelog-2022032903.xml::20220329-03-v2::solo
Liquibase command 'status' was executed successfully.
changeSet
由author
和id
属性以及changelog
文件的位置唯一标识
,是Liquibase
跟踪执行的单位。运行Liquibas
时,它会查询标记为已执行的changSet
的DATABASECHANGELOG
表,然后执行更改日志文件中尚未执行的所有changeSet
。
更改每个changeSet
通常包含一个更改,该更改描述要应用于数据库的更改/重构。Liquibase
支持为支持的数据库和原始SQL
生成SQL
的描述性更改。通常,每个changeSet
应只有一个更改,以避免可能使数据库处于意外状态的自动提交语句失败。
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.7"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog/1.7
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.7.xsd">
<changeSet id="1" author="bob">
<comment>A sample change logcomment>
<createTable/>
changeSet>
<changeSet id="2" author="bob" runAlways="true">
<alterTable/>
changeSet>
<changeSet id="3" author="alice" failOnError="false" dbms="oracle">
<alterTable/>
changeSet>
databaseChangeLog>
每个changeSet
标签都由id
标签、author
标签和changelog
的classpath
名称的组合唯一标签。id
标签仅用作标识符,它不指示更改运行的顺序,甚至不一定是整数。如果您不知道或不希望保存实际作者,只需使用占位符值,如UNKNOW
。
当Liquibase
执行数据库ChangeLog
时,它按顺序读取changeSet
,并针对每个changeSet
检查databasechangelog
表,以查看是否运行了id/author/filepath
唯一标识的组合。如果已运行,则将跳过changeSet
,除非存在真正的runAlways
标签。运行changeSet
中的所有更改后,Liquibase
将在databasechangelog
中插入带有id/author/filepath
的新行以及changeSet
的MD5Sum
。
Liquibase
尝试执行每个changeSet
并在每次结束时提交事务,或者如果出现错误,则回滚。某些数据库将自动提交语句,这些语句会干扰此事务设置,并可能导致意外的数据库状态。因此,通常最好每个changeSet
只进行一次更改,除非有一组非自动提交更改要应用为事务(如插入数据)。
Liquibase
使用databasechangelog
表来跟踪已运行的changeSet
。changelog
文件的路径的id
、author
和filename
列的组合标识。列 | 标准数据类型 | 描述 |
---|---|---|
ID | VARCHAR(255) | changeSet中的id属性值 |
AUTHOR | VARCHAR(255) | changeSet中的author属性值 |
FILENAME | VARCHAR(255) | changelog的路径。这可能是一个绝对路径或一个相对路径,具体取决于changelog如何传递到 Liquibase。为获得最佳结果,它应该是一个相对路径 |
DATEEXECUTED | DATETIME | 执行changeSet的日期/时间。与 ORDEREXECUTED 一起使用以确定回滚顺序 |
ORDEREXECUTED | INT | 执行changeSet的顺序。除 DATE EXECUTED外,还用于确保顺序正确,即使数据库日期时间支持较差的精度也是如此。 注: 仅在单个更新运行中保证值增加。有时,它们会在零处重新启动。 |
EXECTYPE | VARCHAR(10) | changeSet是如何执行的描述。可能的值有EXECUTED, FAILED, SKIPPED, RERAN, 和 MARK_RAN |
MD5SUM | VARCHAR(35) | 执行changeSet时的校验和。用于每次运行,以确保changelog文件中的 changSet 没有意外更改 |
DESCRIPTION | VARCHAR(255) | changeSet生成的可读的描述 |
COMMENTS | VARCHAR(255) | changeSet的comment标签的值 |
TAG | VARCHAR(255) | changeSet的跟踪对应于标签操作 |
LIQUIBASE | VARCHAR(20) | 用于执行changeSet的 Liquibase 版本 |
CONTEXTS | VARCHAR(255) | 用于执行变更集的上下文 |
LABELS | VARCHAR(255) | 用于执行变更集的标签 |
DEPLOYMENT_ID | VARCHAR(10) | 一起部署的变更集将具有相同的唯一标识符 |
Liquibase
使用databasechangeloglock
表确保一次只运行一个Liquibase
实例。
因为Liquibase
只是从databasechangeloglock
表读取以确定需要运行的changeSet
,因此,如果同时对同一数据库执行多个Liquibase
实例,则会发生冲突。如果多个开发人员使用相同的数据库实例,或者集群中有多个服务器在启动时自动运行Liquibase
,则可能会发生这种情况。
如果Liquibase
未干净地退出,则锁住的行可能会保留为锁定状态。您可以通过运行UPDATE DATABASECHANGELOGLOCK SET LOCKED=0
清除当前锁
列 | 标准数据类型 | 描述 |
---|---|---|
ID | INT | 锁的 ID。目前只有一个锁,但将来是有用的 |
LOCKED | INT | 如果 Liquibase正在针对此数据库运行,则设置为"1"。否则设置为"0" |
LOCKGRANTED | DATETIME | 获取锁的日期和时间 |
LOCKEDBY | VARCHAR(255) | 被谁锁住的描述 |
经测试在并发情况下执行正常
集成到项目中,如果sql或者xml书写语法有错误的话,项目启动会报错,服务不会正常启动
可利用liquibase的前提条件判断来解决
案例:
<changeSet id="20220329-05" author="solo">
<preConditions onFail="CONTINUE" onError="CONTINUE" onFailMessage="这是一个失败信息" onErrorMessage="这是一个错误信息">
<sqlCheck expectedResult="0">
select count(*) from b3 where id = 1
sqlCheck>
preConditions>
<sql>
INSERT INTO b3 (id, lac, ci, upflow) VALUES(1, 1, 1, 1);
sql>
changeSet>
在这个例子中,当id为1的记录不存在时,才执行insert的操作。
详细的介绍
https://docs.liquibase.com/concepts/changelogs/preconditions.html#onFail/onError
https://docs.liquibase.com/workflows/liquibase-community/using-the-jenkins-pipeline-stage-with-spinnaker.html
https://www.liquibase.com/pricing?_ga=2.78940244.1580192541.1648448862-1574359928.1646621122
有pro、Business、Enterprise三种
https://www.liquibase.com/pricing/pro
https://www.liquibase.com/pricing/business
https://www.liquibase.com/pricing/enterprise