一般过程
我们为特定版本(例如v1.0
)创建基线。基线包括一个完整的模式创建脚本,以及允许的先前版本的升级脚本(如果有的话)(稍后会详细介绍)。所以对于v1.0
,我们只有一个脚本:
baseline-v1.0.sql
从该基线开始,我们创建增量更改脚本,就像我们从以前的基线开始工作一样。这些脚本以可重入的方式创建,因此它们可以安全地多次运行(第一次只做任何实际工作;请参阅下一段关于如何做的建议)。我们只需为每个更改脚本创建一个文件,其中包含基线名称和时间戳(我们称之为版本)。例如,假设我们在基线之后创建了两个更改脚本。我们有以下文件:
baseline-v1.0.sql (for creating new installations)
baseline-v1.0-201211071220.sql (created on Nov. 7, 2012 at 12:20 PM UTC)
baseline-v1.0-201211122019.sql (created on Nov. 12, 2012 at 8:00 PM UTC)
我们创建一个schema_version
包含两列的表:baseline
和version
。baseline
是一些标签(例如v1.0
上面提到的),并且version
只是创建更改脚本时的时间戳(我们选择这样做是因为创建任意版本号会产生烦人的管理开销,而时间戳很容易使用)。因此,在运行更改脚本之前,我们通过baseline
and查询来检查更改脚本是否已被应用version
。如果它已经存在,只需退出脚本或其他任何内容。否则,应用更改并插入到schema_version
表中以标记更改脚本已完成。
示例更改脚本:
-- Created by <developer> on Nov. 7, 2012 at 12:20 PM UTC
declare @schema_baseline varchar(10), @schema_version varchar(12)
set @schema_baseline = 'v1.0'
set @schema_version = '201211071210'
if exists (select 1 from schema_version where baseline = @schema_baseline and version = @schema_version = @schema_version) return 0
-- begin change script
-- place your schema changes here
-- end change script
insert into schema_version(@schema_baseline, @schema_version)
现在,当我们实际安装软件时,我们运行相关baseline
脚本。当我们升级那个版本时,我们只是按顺序应用更改脚本。
当我们在产品开发阶段达到一个重要的里程碑时,我们会创建一个新的基线。因此,我们创建了一个新的基线脚本(同样,这是作为基线的数据库的快照),以及来自先前基线的升级脚本。所以假设我们有一个新的基线,v2.0
我们会有以下文件:
baseline-v2.0.sql (for creating new installations)
baseline-v2.0-upgrade-v1.0.sql (for upgrading from v1.0)
然后该过程继续。
我们如何应用更改
脚本都保存在源代码管理中。我们确实有一个工具可以打包这些文件并自动升级我们的支持和安装团队使用的数据库。该工具计算出目标数据库的当前基线,并询问用户是否希望升级到包中的基线。如果他们这样做了,并且当前版本存在有效的升级路径,它会应用升级脚本,并更新schema_version.baseline
,并从之前的基线中删除所有更改脚本条目。如果数据库是新的,它将应用常规基线脚本。无论哪种方式,在实现基线后,它都会应用程序包中存在的基线中的所有更改脚本,一次一个,按顺序,在事务中。如果特定更改脚本失败,它将回滚最后一组更改和错误。我们查看日志,修复所有问题,然后再次重新运行包。那时,它应该只在最后一个成功的更改脚本处启动,从而节省时间。
自动化和差异工具
我们不允许差异工具直接升级生产数据库。这太冒险了。当然,我们确实使用 diff 工具来帮助创建我们的升级和更改脚本,但是一旦有了它们,我们就会梳理它们、按摩它们、测试它们等,然后根据上面的规范创建升级或更改脚本. 我们确实使用工具/shell 脚本来创建更改脚本文件并进行样板schema_version
检查。
注意事项
它实际上非常简单,而且效果很好。唯一真正变得棘手的是分支。在大多数情况下,分支处理得很好。如果我们需要为特定分支的工作更改脚本,一旦我们将分支合并回来,它将很好地折叠到主线中。没问题。当两个分支尝试做类似的事情时,或者一个分支依赖于另一个分支时,它变得棘手。不过,这主要是一个流程和计划问题。如果我们陷入这种情况,我们只需创建一个新基线(例如v2.1
),然后相应地更新分支。
要记住的另一件事是,如果安装想要从一个基线升级到另一个基线,它必须在我们升级到新基线之前应用当前基线的所有未完成更改。换句话说,我们不会让安装从它们所在的位置直接跳转到下一个基线(当然,除非它们已经是当前基线的最新版本)。