介绍
Liquibase是一款成熟的数据库版本控制工具,支持多种数据库,多种描述文件格式,以及多种数据库操作手段(原生change type、Java自定义change type)。
在单体服务中,应用Liquibase是一件很轻松的事情,只需要按照要求配置好相关属性即可。但是在微服务场景下,就需要仔细考量一番。
单体服务
在单体服务中,通常只使用一种数据库的一个实例,在该情况下,如果是Spring应用,则只需要在application.yml中设置必要属性,然后服务启动的时候,会自动执行Liquibase的update操作。
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/db-test
username: root
password: 123456
liquibase:
change-log: classpath:db/changelog/changelog.xml
因为spring.liquibase.change-log
指向了classpath:db/changelog/changelog.xml
,所以在src/main/resources
目录下,需要创建db/changelog/changelog.xml
文件。
以下是一个官方的示例:
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"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<changeSet id="1" author="saas">
<createTable tableName="test_table">
<column name="test_id" type="int">
<constraints primaryKey="true"/>
</column>
<column name="test_column" type="varchar(255)"/>
</createTable>
</changeSet>
</databaseChangeLog>
微服务
在微服务场景下,其实主要的问题,是如何放置changelog文件,可以有两种方案:
集中放置
方式:将所有微服务使用到的所有changelog文件集中放在一个地方(比如:gitlab上的一个独立项目)。目录结构大致如下:
lua
liquibase
+-- changelog-root.xml
+-- <module-name>
+-- changelog-1.0.xml
+-- changelog-1.1.xml
即:
- changelog-root.xml是入口文件,它会includes所有子目录下其他changelog文件
- 每个模块做成一个目录
- 在每个模块目录下,是按照版本划分的changelog文件
适用场景
适合所有微服务使用同一个数据库实例的场景。
部署说明
在部署到k8s时,为了让每个pod都能访问到这些集中放置的changelog文件,可以使用PV或者DaemonSet的方式,让这些changelog文件对pod可见,而且使用统一的目录名。
局限性
将来一旦有微服务必须使用单独的数据库实例,那么就必须再设计一套独立的changelog目录,从而打破了集中放置的初衷。
分散放置
方式:每个微服务都可能有一套changelog文件,放置在每个微服务的src/main/resources/liquibase目录下。目录结构大致如下:
lua
src/main/resources/liquibase
+-- changelog-root.xml
+-- <微服务名>
+-- changelog-1.0.xml
+-- changelog-1.1.xml
即:
- changelog-root.xml是入口文件,它会includes所有子目录下其他的changelog文件
- <微服务名>建议使用${spring.application.name},以区分不同的微服务(为什么需要一个微服务名的目录?为的是将来万一要按照模块集中放置changelog文件,可以很方便的拷贝并合并这些文件)
- 每个<微服务名>目录下,是按照版本划分的changelog文件
适用场景
适合每个微服务使用独立数据库实例的场景。当然,它也兼容所有微服务使用同一个数据库实例的场景,甚至是混合场景。
而且最好一个人负责一整个模块,而不要只负责某个模块的部分微服务。
部署说明
不需要特殊的部署
局限性
这种方式提供了很高的灵活性和可扩展性,但同时也提高了开发的复杂性和维护难度,体现在:
- 每个模块的不同微服务之间,必须确保不会操作相同的表
- 在一个人维护多个模块的情况下,需要在不同模块之间切换,容易产生"手误"
结论
在微服务场景下,如果使用了集中放置changelog文件的方案,则需要特定的部署(PV或者DaemonSet)来配合。如果使用了分散放置changelog文件的方案,则开发方式与单体服务没有区别,问题就在于通过管理手段实现微服务之间表冲突的问题。