一文详解Liquibase如何自动化数据库脚本部署 译文
其他数据库 自动化
本文通过实例的形式,向您介绍并展示了一种能够实现自动化脚本部署的数据库产品--Liquibase。

【51CTO.com快译】您是否还在手动对数据库执行各种脚本?您是否还在浪费时间去验证数据库脚本的正确性?您是否还需要将脚本合并到某个文件中,以便在每个环境中执行?在面对部署错误时,您是否需要花费数小时去查看数据库的更改,以定位原因?

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

Liquibase的基本特点

  • 自动化数据库的部署脚本。
  • 以相同的方式部署到不同的环境中。
  • 能够为每次数据库的更改准备好回滚。
  • 能够将部署的所有详细信息集中到一处。
  • 最少化的部署错误。
  • 方便开发人员针对相同的数据库,进行高效的协同编程。
  • 审核逐个更改(请参考下图)。

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

什么是Liquibase?

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

  • DATABASECHANGELOG — 存储有关数据库所有更改信息的表。
  • DATABASECHANGELOGLOCK — 用于防止用户同时对数据库进行更改。

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

从安装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。

配置项目和Liquibase

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

Liquibase中的文件组织

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

更新了的Liquibase文件夹结构

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

  1. <?xml version="1.0″ encoding="UTF-8″?><databaseChangeLog 
  2. xmlns="http://www.liquibase.org/xml/ns/dbchangelog" 
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4. xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog 
  5. http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd"></databaseChangeLog> 

使用 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的路径):

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

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

将Liquibase与数据库连接

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

  1. 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文件:

  1. <?xml version="1.0″ encoding="UTF-8″?> 
  2. <databaseChangeLog 
  3. xmlns="http://www.liquibase.org/xml/ns/dbchangelog" 
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  5. xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog 
  6. http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd"> 
  7. <includeAll path="triggers" relativeToChangelogFile="true"/> 
  8. <includeAll path="views" relativeToChangelogFile="true"/> 
  9. <includeAll path="types" relativeToChangelogFile="true"/> 
  10. <includeAll path="package_spec" relativeToChangelogFile="true"/> 
  11. <includeAll path="package_bodies" relativeToChangelogFile="true"/> 
  12. </databaseChangeLog> 

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

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

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

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

跟踪DML和DDL数据库的更改

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

  1. —Liquibaseformatted sql 

将changelog.sql标记为LiquibaseSQL文件

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

指向新的changelog

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

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

  1. —changeset AUTHOR:CHANGESET_NAME 
  2. —comment OPTIONAL COMMENT 
  3. YOUR DDL 

创建第一个项目表

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

  1. Liquibase—defaultsFile=liquibase_dev.properties updateSQL 

由Liquibase生成SQL文件

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

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

  1. Liquibase—defaultsFile=liquibase_dev.properties update 

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

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

跟踪包、视图等其他更改

我们也可以如法创建其他脚本。在此,我通过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等变更集,去更新相应的行。

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

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

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

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

  1. P_ADD_JOB_HISTORY 
  2. P_SECURE_DML 
  3. TRG_SECURE_EMPLOYEES 
  4. TRG_UPDATE_JOB_HISTORY 

现有的P_ADD_JOB_HISTORY.sql文件

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

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

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

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

  1. Liquibase—defaultsFile=liquibase_dev.properties updateSQL 

首次尝试更新SQL

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

  1. Liquibase—defaultsFile=liquibase_dev.properties ChangelogSyncSQL 

输出的SQL文件为:

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

  1. 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安装到拥有数百个对象的、现有数据库中的最佳方式。不过,当涉及到库里有不能被替换的对象(如表格)时,我们需要使用另一种方式。

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

创建或替换的对象

正如前面所描述过的,请添加对象,并在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来实现数据库的自动化。

小结

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

  • 基于变更集的唯一性,并结合AUTHOR:ID(task)与文件名(带changelog的文件),将变更集添加到changelog中。而且,请不要在没有Liquibase的情况下,更改任何内容。
  • 验证待执行的SQL。
  • 运行数据库的update命令(记住,应当先运行updateSQL,再执行update命令)。
  • 通过检查数据库对象和DATABASECHANGELOG表,验证变更集是否已被执行。

原文标题:What is Liquibase? How to Automate Your Database Script Deployment,作者:Rafal Grzegorczyk

【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】

 

责任编辑:华轩 来源: 51CTO

编辑推荐

几款开源的图形化Redis客户端管理软件推荐NoSQL数据库概览及其与SQL语法的比较一文看懂分布式数据库原理和 PostgreSQL 分布式架构目前好用的五大PostgreSQL GUI一文揭秘单线程的Redis为什么这么快?
我收藏的内容
点赞
收藏