Mental Model#
学习描述 Liquibase 执行模型的主要概念。
Changes#
Liquibase 管理数据库变更的执行。一次变更可以是目标数据库所支持的方言写成的简单语句。例如 “SQL” 变更(在 liquibase-neo4j 中别名为 Cypher)。
一次变更也可以是复杂的转换操作,例如 “Rename Table”。
复杂变更的价值体现在两个方面:
- 它们封装了多个基本操作,避免了手动编写时的繁琐与出错风险
- 大多数复杂变更能够在不同的数据库管理系统之间移植
注意
对 liquibase-neo4j 来说,可移植性不是问题。liquibase-neo4j 仅支持 Neo4j,暂无计划支持其他图数据库。随着 即将到来的 GQL 标准 成熟并被生态系统充分采用,这一点可能会改变。
Change Sets and Transactions#
变更会被归入变更集(change set)。
默认情况下,同一变更集中的所有变更在同一个事务中执行——要么全部成功,要么全部失败。
重要提示
Neo4j 的 模式操作 与数据操作不能放在同一个事务中,最好将它们拆分到不同的变更集。
可以通过将变更集的 runInTransaction 属性设为 false 来关闭此行为。此时,变更集中的每个变更都会在各自的自动提交事务中执行。
了解关于 runInTransaction 使用 的最佳实践。
Change Logs and Consistency#
变更集定义在变更日志(change log)中,通常以平面文件的形式存放在本地文件系统。
一个变更日志可以包含其它变更日志,亦或直接定义变更集。
变更日志文件可以使用 多种格式 编写,甚至可以混合使用(例如:顶层 XML 变更日志中可以包含 SQL 变更日志)。
注意
每个变更集通过 ID、作者名称以及定义它的变更日志路径唯一标识,从而避免意外冲突。
Liquibase 运行时只接受一个变更日志入口点,并会解析所有包含的变更日志,生成一个线性的变更集序列。
变更集之间可能存在依赖关系。例如,前一个变更集创建了一张表,后一个变更集则假设该表已存在并向其插入种子数据。
在分布式系统中实现此类依赖所需的“一致性模型”称为 因果一致性,有时也被称作 “Read Your Own Write”。
对于 Neo4j,书签(bookmark)机制满足了上述属性。书签随事务一起传递,用来标识目标实例在处理该事务前必须达到的状态。
基于此理念,原生 Neo4j 驱动定义了会话(session)。会话绑定到单个执行线程,并自动将前一次成功事务的书签链入下一次事务。
liquibase-neo4j 使用的 JDBC 连接器依赖原生 Java 驱动,并将单个 JDBC 连接绑定到单个 Neo4j 会话。一次 Liquibase 调用只会打开一个连接。因此,因果一致性属性会自动得到保障。
Persistence and History Graph#
默认情况下,Liquibase 以“追加”方式执行变更。也就是说,与其修改已有的变更集,通常的做法是向现有序列追加新的变更集。
Tip
默认禁止修改变更集。变更集是不可变的。若希望其可变,需要将变更集的属性 runOnChange 设置为 true。
Liquibase 默认不会重新运行已执行过的变更集。它们一旦在目标数据库上执行完毕,就不会再次运行。
Tip
若希望重新运行变更集,需要将其属性 runAlways 设置为 true。
已执行的变更集必须被持久化。liquibase-neo4j 将其存储为历史图(在关系型数据库中对应的是 DATABASECHANGELOG 表)。
注意
liquibase-neo4j 将该历史图存放在与变更日志运行所在的同一数据库(租户)中。不支持在一个数据库上执行变更日志、在另一个数据库中持久化历史的情形。
重要提示
liquibase-neo4j 不保证历史图的模式在版本之间(即使是非主版本升级)保持不变。用户不应直接操作历史图。如果模式发生变化,liquibase-neo4j 会自动对先前版本持久化的历史图执行内部迁移。
Concurrency#
liquibase-neo4j 不应在同一服务器和同一数据库上并发执行。类似于 DATABASECHANGELOGLOCK 表 的机制,liquibase-neo4j 会尝试创建唯一的 __LiquibaseLock 节点。若创建失败,则说明已有其他执行正在进行,此时当前执行会被终止。