一文详解Liquibase如何自动化数据库脚本部署

百家 作者:51CTO技术栈 2021-12-21 18:31:51
作者丨Rafal Grzegorczyk
译者丨陈骏
策划丨孙淑娟
您是否还在手动对数据库执行各种脚本? 您是否还在浪费时间去验证数据库脚本的正确性? 您是否还需要将脚本合并到某个文件中,以便在每个环境中执行? 在面对部署错误时,您是否需要花费数小时去查看数据库的更改,以定位原因?

如今,大多数组织都已经在其应用程序中实施了 DevOps 的 CI/CD 流程。不过,其数据库的自动化改造似乎尚未跟上时代。为此,我将向您介绍一种能够实现自动化脚本部署的数据库产品 --Liquibase。

1

Liquibase 的基本特点

  • 自动化数据库的部署脚本。

  • 以相同的方式部署到不同的环境中。

  • 能够为每次数据库的更改准备好回滚。

  • 能够将部署的所有详细信息集中到一处。

  • 最少化的部署错误。

  • 方便开发人员针对相同的数据库,进行高效的协同编程。

  • 审核逐个更改 (请参考下图)。

下面,我将向您展示如何使用 Liquibase 和 Git 在 Pretius 上,自动化数据库的更改过程。

2

什么是 Liquibase?

Liquibase(简称 LB) 是一个用 Java 编写的开源工具。它以用户熟悉的格式定义了数据库接口,并能够自动生成特定于数据库的 SQL。例如,它将数据库的更改 (每一次更改可称为一个更改集) 放入被称为 changelog 的文件中进行管理。通常,Liquibase 在数据库架构中会自动创建两张表:

  • DATABASECHANGELOG — 存储有关数据库所有更改信息的表。

  • DATABASECHANGELOGLOCK — 用于防止用户同时对数据库进行更改。

我将在下面示例中,基于 SQL 编写变更集,以实现对 Oracle 数据库的自动化更改过程。

3

从安装 Liquibase 开始

请通过链接 https://www.liquibase.org/download,选择“仅文件 (Just the files)”的方式,下载 Liquibase 的最新版本。在本文中,我将使用版本:4.3.0 build 09.02.2021。

在将其 zip 文件夹解压缩后,您必须将新的路径变量 (New Path System Variable) 设置为计算机上的 liquibase-version#bin 文件夹。同时,为了使 Liquibase 正常工作,您还必须安装 Java。

通过在 CLI 工具 (在此,我使用的是 Visual Studio Code) 输入:Liquibase—version,您将能看到:

如果您在文件中使用的是 UTF8 编码,那么请务必在 liquibase.bat 文件中添加一行:IF NOT DEFINED JAVA_OPTS set JAVA_OPTS=-Dfile.encoding=UTF–8。

4

配置项目和 Liquibase

下面,让我们来组织各个文件 (在本例中,我的 GIT 存储库放在文件夹 HR 中)。在各个文件夹中,我们可以在项目开发的过程中创建不同的文件。如果您有其他类型的对象 (如“创建或替换”类型),那么只需要用它们创建“同义 (synonyms)”文件夹即可。

Liquibase 中的文件组织

#path to our master changelog file changeLogFile:Liquibase/update.xml #dbhost and credentials url:  jdbc:oracle:thin:@127.0.0.1:1521/XEPDB1 username: HR password: XXXXXX #OJDBC driver localization classpath:Liquibase/ojdbc8.jar #schema, whereLiquibasewill store it’s DATABASECHANGELOG and DATABASECHANGELOGLOCK table(if other than HR, remember to add grants to HR!) liquibaseSchemaName: HR #default  SQL file name generated byLiquibase outputFile=output_local.sql #debug mode loglevel=SEVERE #extra option fromLiquibase, we don’t need it for now. liquibase.hub.mode=off

更新了的 Liquibase 文件夹结构

现在,我们创建一个 update.xml 文件,并将它放入带有 OJDBC 文件的、新的 hr/Liquibase 文件夹中:

<?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.3.xsd"></databaseChangeLog>

5

使用 Oracle Wallet(可选)

如果您的 Oracle 数据库托管在 Oracle 自治数据库上,那么就需要使用 Wallet,通过 Liquibase 去连接它。为此,请下载 Wallet 并记住其密码。

请将您的 WALLET_NAME.ZIP 解压到之前创建的 HR/Liquibase 文件夹中,并编辑 HR/liquibase/wallet_name/ojdbc.properties 文件:

更改 ojdsb.properties

修改后的文件如上图所示。在 javax.net.ssl.trustStorePassword 和 javax.net.ssl.keyStorePassword 行,你可以设置 ATP Wallet 的密码。

在 liquibase_local.properties 文件中,请编辑 URL 一行,并设置连接的名称 (即,来自 Wallet/tnsnames.ora,以及去往 Wallet 的路径):

url: jdbc:oracle:thin:@rgatp28_high?TNS_ADMIN=liquibase/Wallet_RGATP28

当然,请检查您的 sqlnet.ora 文件,确保其“SSL_SERVER_DN_MATCH=yes”,且无需改变其他地方。

6

将 Liquibase 与数据库连接

如果一切设置正确,我们便可以顺利连接上 DEV 数据库。让我们从 HR 文件夹 (Liquibase 的属性文件位置) 处启动 CLI,并输入:

Liquibase—defaultsFile=liquibase_dev.properties updateSQL

VSCode 终端中的 updateSQL 命令

其中:

  • Liquibase 会调用 LB 的环境路径。

  • defaultsFile 指定属性文件的名称和位置。如果您将属性文件命名为“liquibase.properties”,那么可以省略此命令。

  • updateSQL 负责生成 SQL 脚本 (它并不会对数据库执行任何操作)。

几秒钟后,LB 将会生成 output_file.sql:

生成的 output_file.sql

如前所述,如果您在数据库中运行该脚本,它将创建两个表:DATABASECHANGELOG 和 DATABASECHANGELOGLOCK。下面,让我们通过 Liquibase—defaultsFile=liquibase_dev.properties update,来创建这些表。其中的 update 命令是对数据库执行 SQL 语句。完成后,您将看到如下结构:

我们需要创建一个 changelog 文件,并指向包含对象的文件夹。在此,我创建了如下 HR/master.xml 文件:

<?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.3.xsd"> <includeAll path="triggers" relativeToChangelogFile="true"/> <includeAll path="views" relativeToChangelogFile="true"/> <includeAll path="types" relativeToChangelogFile="true"/> <includeAll path="package_spec" relativeToChangelogFile="true"/> <includeAll path="package_bodies" relativeToChangelogFile="true"/> </databaseChangeLog>

它指向对象文件夹、及其所有内容。为了将主 changelog 文件 HR/liquibaseupdate.xml 设置为指向 master.xml 文件的路径,您只需添一行:

<include file="./master.xml"/>

在 update.xml 中的 include file="./master.xml"

由于 Liquibase 始终会从 Liquibase_dev.properties 文件和 update.xml 文件处运行,因此我们需要让它能够“看到”所有的文件。

7

跟踪 DML 和 DDL 数据库的更改

我们需要为 DML 和 DDL 类型的更改创建一个单独的 changelog 文件,并将更改集写入其中。为此,我们只需创建一个 changelog.sql 文件,并输入如下内容,以将其标记为 LiquibaseSQL 文件:

—Liquibaseformatted sql

将 changelog.sql 标记为 LiquibaseSQL 文件

我们通过在 master.xml 文件中添加如下内容,以指向新的 changelog:

指向新的 changelog

指向 changelog 或文件夹的顺序是非常重要的。它需要告知 Liquibase 在运行 SQL 时的顺序。我们最好先运行 changelog(其中包含了“create table(...)”),然后再运行使用该表的编译包。

下面,让我们在变更集中创建第一个项目表:

—changeset AUTHOR:CHANGESET_NAME comment OPTIONAL COMMENT YOUR DDL 

创建第一个项目表

为了预览到数据库有哪些更改,我们让 LB 生成对应的 SQL 文件。

Liquibase—defaultsFile=liquibase_dev.properties updateSQL

由 Liquibase 生成 SQL 文件

您可能注意到了,LB 通过设置 LOCKED = 1,来锁定 DATABASECHANGELOGLOCK 表。也就是说,当您将脚本运行到 DB 时,列 LOCKED 被设置为 1。而当另一个用户同时运行 LB 时,Liquibase 将为此等待,直到锁定被放开,再创建一个 SHOES 表,将日志的更改插入到 DATABASECHANGELOG 中,并从 DATABASECHANGELOGLOCK 表中释放掉已有的锁。

如果一切正常,如下脚本会被执行到数据库中:

Liquibase—defaultsFile=liquibase_dev.properties update

接着,表 SHOES 会被创建出来。

我们也可以查询到谁、为何、以及何时创建了这张表。

8

跟踪包、视图等其他更改

我们也可以如法创建其他脚本。在此,我通过 2 个单独的文件,创建了一个 SHOES_PKG 包。每个文件都是带有附加参数的唯一变更集,并被标记为 Liquibase 格式的 SQL 文件。

SHOES_BODY 和 SHOES_SPEC SQL 文件

其中:

  • runOnChange:true ——意味着每次更改包时,Liquibase 都会针对数据库运行该变更集,也就是编译这个包。

  • stripComments:false ——意味着不要去除代码注释。

因此,LB 在对数据库进行 updateSQL 操作时,就会去编译包的规范 (package spec)、以及包的主体 (package body)。一旦我们在数据库中通过 update 命令编译这些包,它们都会被记录下来。

通过查看 MD5SUM 的列值可知,它是变更集的最后一次校验。也就是说,运行了 updateSQL 后,所有前期被“挂起”的更改都被执行,而且除了锁定 LB 表外,LB 不会在 SQL 中生成任何内容。

运用 updateSQL 检查 output_local.sql

现在,让我们改变 SHOES_PKG 本身,并保存该文件。

更新 SHOES_PKG 本身

那么该文件的校验和会发生变化,LB 将再次编译这个包,并运行更新。

Liquidbase 中的更新



数据库中的更新

Liquibase 将再次编译这个包,并使用 DATABASECHANGELOG 表中的实际 DATEEXECUTED 和新的 MD5SUM 等变更集,去更新相应的行。

9

如何在现有软件项目中安装 Liquibase?

虽然我们好几种方法可以让 Liquibase 为现有的数据库实现自动化,但是我在此只向您展示最实用的两种。您可以从中选择最适合实际需求的一种。

10

当现有的项目中有很多对象时

我们通过在项目的存储库中配置 Liquibase,并保留所有文件的基础上,在 master.xml 文件中添加指向它们的路径。具体而言,在实施 Liquibase 之前,我创建了 2 个过程和 2 个触发器:

P_ADD_JOB_HISTORY P_SECURE_DML TRG_SECURE_EMPLOYEES TRG_UPDATE_JOB_HISTORY 

现有的 P_ADD_JOB_HISTORY.sql 文件

您并不需要将“changeset”或“–Liquibaseformatted sql”添加到文件中。

更新后 master.xml 中的文件路径

我在自己的 master.xml 中添加了一个指向 PROCEDURES 文件夹的路径。

下面,让我们运行 LiquibaseupdateSQL,并查看 Liquibase 会执行什么样的 SQL:

Liquibase—defaultsFile=liquibase_dev.properties updateSQL

首次尝试更新 SQL

既然我们的数据库中已经有了这些过程和触发器,我们就需要通过 ChangelogSync 和 ChangelogSyncSQL 命令,避免重复创建。让我们运行 ChangelogSyncSQL,并查看其结果。

Liquibase—defaultsFile=liquibase_dev.properties ChangelogSyncSQL

输出的 SQL 文件为:

可见,SQL 文件只插入了一个 DATABASECHANGELOG 表。它会告知 Liquibase 这些对象已经创建好了,不需要再次运行。现在,我们便可以将其插入到 Oracle 数据库中了:

Liquibase—defaultsFile=liquibase_dev.properties ChangelogSync

此时,在 DATABASECHANGELOG 表中会有 4 个新的变更集:

您也许会问,这些奇怪的“raw”ID 是什么? 为什么作者又被称为“includeAll”呢? 这是因为我们采取了最简单、最快捷的方式,将现有的项目迁移到了 Liquibase 处,而这些变更集是被自动创建的。

当然,您也可以进行一些更改。例如,在 P_ADD_JOB_HISTORY 中,只需添加一个 changeset,就像您在创建新数据库对象时常做的那样。

更改 P_ADD_JOB_HISTORY

然后运行 Liquibase 的更新命令:

现在,Changeset 就带有了合适的作者、ID 等信息。

在上面的示例中,我向您展示了添加现有对象 (可创建或替换) 的简单方法,且无需手动创建变更集。我认为这是将 Liquibase 安装到拥有数百个对象的、现有数据库中的最佳方式。不过,当涉及到库里有不能被替换的对象 (如表格) 时,我们需要使用另一种方式。

11

创建或替换的对象

正如前面所描述过的,请添加对象,并在 master.xml 文件中记下文件夹的路径。接着请运行 ChangelogSync,并让 Liquibase 自动创建 raw/includeAll/filename 的变更集。

由 Liquibase 生成的变更集

当然,您也可以采用更好的方法,为每个文件创建一个变更集,如下图所示:

虽然这会需要更多的工作,但是您可以在日志中获得更全面的信息:

针对无法创建或替换的对象,您同样有两种方法:

  • 对这些对象不做任何操作,但请记住始终为其中的每个更改 (包括:更改表、删除列等) 创建变更集,并将其添加到 changelog.sql 文件中。

  • 创建变更集并将它们标记为过去已被执行过。

在此,我们着重讨论第二种方式。由于在实施 Liquibase 之前我已创建好了 EMPLOYEES 和 JOBS 两张表,因此我会在新的文件夹 HR/scripts_before_Liquibase 中,创建 changelog_ddl.sql 和 changelog_constraints.sql 两个 changelog 文件。此外,我也创建了另一个 scripts_before_liquibase.xml 文件,并将其指向这两个 changelog。其中的“include file”通过优先级的方式,告知 Liquibase 运行脚本的顺序,即:首先创建表,然后创建约束和索引。

新的 scripts_before_liquibase.xml 文件

这两个文件能够方便您在表中创建 ref_constraint 时,避免产生冲突。如下图所示,请记住在 master.xml 文件中,将路径添加到新创建的 XML 文件 (即 HR/script_before_liquibase/scripts_before_liquibase.xml) 中。

下面是为各种表和约束创建的变更集。

在添加了所有的变更集之后,我们将它们标记为已执行的状态。

让我们运行 ChangelogSyncSQL 来进行预览,并让 ChangelogSync 对数据库执行 SQL。

运行 ChangeSyncSQL 和 ChangelogSync

下图展示了 ChangelogSync 命令执行后,更新了的数据库。至此,我们已大功告成,您也可以选用自己喜欢的方式,通过 Liquibase 来实现数据库的自动化。

12

小结

如您所见,通过使用 Liquibase,我们可以在数据库更改的发布过程中,跟踪所有的相关内容。其中,需要开发人员遵守如下流程:

  • 基于变更集的唯一性,并结合 AUTHOR:ID(task) 与文件名 (带 changelog 的文件),将变更集添加到 changelog 中。而且,请不要在没有 Liquibase 的情况下,更改任何内容。

  • 验证待执行的 SQL。

  • 运行数据库的 update 命令 (记住,应当先运行 updateSQL,再执行 update 命令)。

  • 通过检查数据库对象和 DATABASECHANGELOG 表,验证变更集是否已被执行。


关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接