跟踪数据库架构更改的机制


135

跟踪和/或自动执行数据库架构更改的最佳方法是什么?我们的团队使用Subversion进行版本控制,并且我们已经能够以这种方式自动执行某些任务(将构建推送到登台服务器,将经过测试的代码部署到生产服务器),但是我们仍在手动进行数据库更新。我想找到或创建一个解决方案,使我们能够在具有不同环境的服务器之间高效地工作,同时继续使用Subversion作为后端,通过该后端代码和数据库更新被推送到各种服务器。

许多流行的软件包都包括自动更新脚本,该脚本可检测数据库版本并应用必要的更改。这是在更大范围内(跨多个项目,有时跨多种环境和语言)执行此操作的最佳方法吗?如果是这样,是否有任何现有代码可以简化流程,或者最好只是推出我们自己的解决方案?之前有没有人实现过类似的东西并将其集成到Subversion提交后钩子中,还是一个坏主意?

尽管支持多个平台的解决方案是更可取的,但是我们绝对需要支持Linux / Apache / MySQL / PHP堆栈,因为我们的大部分工作都在该平台上。

Answers:


56

在Rails的世界中,存在迁移的概念,即脚本,其中使用Ruby而不是特定于数据库的SQL风格对数据库进行更改。您的Ruby迁移代码最终将转换为特定于您当前数据库的DDL;这使得切换数据库平台非常容易。

对于您对数据库所做的每一次更改,您都要编写一个新的迁移。迁移通常有两种方法:应用更改的“上”方法和撤消更改的“下”方法。单个命令可以使数据库更新,也可以用于将数据库带到架构的特定版本。在Rails中,迁移将保留在项目目录中其自己的目录中,并像其他任何项目代码一样被签入版本控制。

该Oracle Rails迁移指南很好地介绍了迁移。

使用其他语言的开发人员已经研究了迁移并实现了自己的特定于语言的版本。我知道Ruckusing是一个PHP迁移系统,该系统以Rails的迁移为模型。这可能是您要寻找的。


1
摇摇欲坠的FTW-我们将其调整为适合我们的数据库系统,对此感到非常满意。
Piskvor于


50

我们使用类似于bcwoord的方法来保持数据库模式在5个不同的安装(生产,暂存和一些开发安装)之间同步,并在版本控制中进行备份,并且效果很好。我会详细说明:


为了同步数据库结构,我们有一个脚本update.php,以及许多编号为1.sql,2.sql,3.sql等的文件。该脚本使用一个额外的表来存储数据库的当前版本号。数据库。N.sql文件是手工制作的,以从数据库的版本(N-1)升级到版本N。

它们可用于添加表,添加列,将数据从旧的格式迁移到新的列格式,然后删除列,插入“主”数据行(例如用户类型)等。基本上,它可以执行任何操作并使用适当的数据迁移脚本,您将永远不会丢失数据。

更新脚本的工作方式如下:

  • 连接到数据库。
  • 备份当前数据库(因为东西出错)[mysqldump]。
  • 如果不存在,请创建簿记表(称为_meta)。
  • 从_meta表读取当前的VERSION。如果未找到,则假定为0。
  • 对于所有编号高于VERSION的.sql文件,请按顺序执行
  • 如果文件之一产生错误:回滚到备份
  • 否则,将簿记表中的版本更新为执行的最高.sql文件。

一切都进入源代码控制,并且每个安装都有一个脚本,可以通过一次脚本执行将脚本更新到最新版本(使用正确的数据库密码调用update.php等)。我们通过自动调用数据库更新脚本的脚本来SVN更新登台和生产环境,因此代码更新随附必需的数据库更新。

我们还可以使用相同的脚本从头开始创建整个数据库。我们只是删除并重新创建数据库,然后运行将完全重新填充数据库的脚本。我们还可以使用脚本填充空数据库以进行自动测试。


设置该系统仅花费了几个小时,从概念上讲它很简单,并且每个人都可以使用版本编号方案,并且它具有前进和发展数据库设计的能力,而无需沟通或手动执行修改,这是无价的。在所有数据库上。

但是当从phpMyAdmin粘贴查询时要当心!这些生成的查询通常包含数据库名称,您绝对不希望使用该数据库名称,因为它将破坏您的脚本!类似于CREATE TABLE mydbnewtable如果系统上的数据库未称为mydb,则(...)将失败。我们创建了一个注释前的SVN钩子,该钩子将禁止包含该mydb字符串的.sql文件,这是一个确定的迹象,表明有人未经适当检查就从phpMyAdmin复制/粘贴了该文件。


您如何处理碰撞?多个开发人员更改了数据库中的同一元素,例如一个存储过程?如果您在同一分支上同时工作,或者有两条开发线(两个分支),则可能发生这种情况
Asaf Mesika 2010年

碰撞非常罕见。真正发生的唯一事情是两个人将尝试创建相同的N.sql文件。当然,第一个获胜,第二个被迫重命名为下一个最高号码,然后重试。但是,我们在分支上没有数据库版本控制。
rix0rrr

12

我的团队编写了所有数据库更改的脚本,并将这些脚本以及应用程序的每个版本都提交给SVN。这允许增量更改数据库,而不会丢失任何数据。

要从一个发行版转到另一个发行版,您只需要运行一组更改脚本,并且数据库是最新的,您仍然可以获得所有数据。这可能不是最简单的方法,但绝对有效。


1
您如何编写所有更改的脚本?
史密斯

10

此处的问题确实使开发人员可以轻松地将自己的本地更改编写到源代码控制中,以与团队共享。我已经面对这个问题很多年了,并且受到Visual Studio for Database专业人士功能的启发。如果您想要具有相同功能的开源工具,请尝试以下操作:http : //dbsourcetools.codeplex.com/ 祝您好运 -Nathan。


10

如果您仍在寻找解决方案:我们建议使用一种名为neXtep designer的工具。这是一个数据库开发环境,您可以通过该环境将整个数据库置于版本控制之下。您在版本控制的存储库上工作,可以跟踪每个更改。

当需要发布更新时,您可以提交组件,产品将自动从先前版本生成SQL升级脚本。当然,您可以从任何2个版本生成此SQL。

然后,您有很多选择:您可以将这些脚本与应用程序代码一起放入SVN中,以便通过现有机制进行部署。另一个选择是使用neXtep的传递机制:脚本以“传递包”(SQL脚本+ XML描述符)的形式导出,安装程序可以理解该包并将其部署到目标服务器,同时确保结构一致性,依赖性检查,注册已安装的版本等。

该产品是GPL,基于Eclipse,因此可以在Linux,Mac和Windows上运行。目前它还支持Oracle,Mysql和Postgresql(正在提供DB2支持)。看看Wiki,您将在其中找到更多详细信息:http : //www.nextep-softwares.com/wiki


看起来很有趣。它是否也具有命令行界面,或者已计划使用?
Piskvor在

8

Scott Ambler撰写了许多有关数据库重构的文章(并合着了),其思想是您应实质上将TDD原理和实践应用于维护架构。您为数据库设置了一系列结构和种子数据单元测试。然后,在您进行任何更改之前,您都要修改/编写测试以反映该更改。

我们已经这样做了一段时间了,而且似乎可行。我们编写了在单元测试套件中生成基本列名和数据类型检查的代码。我们可以随时重新运行这些测试,以验证SVN签出中的数据库是否与应用程序实际运行的实时数据库匹配。

事实证明,开发人员有时还会调整其沙箱数据库,而忽略了更新SVN中的架构文件。然后,代码依赖于尚未签入的数据库更改。这种错误很难确定,但是测试套件会立即将其修复。如果您将其内置到较大的持续集成计划中,则特别好。


7

将模式转储到文件中,并将其添加到源代码管理中。然后一个简单的差异将向您显示发生了什么变化。


1
转储必须使用SQL,就像mysqldump一样,Oracle的转储是二进制的。
乌萨玛·马德

7
模式差异还存在一个更基本的问题。如何区分列拖放+添加和列重命名。答案很简单:您不能。这就是为什么您需要记录实际的架构更改操作的原因。
psp 2010年

差异将显示一列消失了,而另一列出现了(除非它们具有相同的名称),并且在大多数情况下就足够了。当然,编写每个模式更改的脚本是一个好方法:例如,在Drupal中,这是由一个特殊的钩子处理的。
deadprogrammer 2010年


5

这是一项低技术,可能会有更好的解决方案,但是您可以将架构存储在可以运行以创建数据库的SQL脚本中。我认为您可以执行命令来生成此脚本,但不幸的是我不知道该命令。

然后,将脚本及其上的代码提交到源代码管理中。当需要与代码一起更改模式时,可以将脚本与需要更改的模式的代码一起检入。然后,脚本上的差异将指示架构更改上的差异。

使用此脚本,您可以将其与DBUnit或某种构建脚本集成,因此它似乎可以与您已经自动化的流程配合使用。


是的,这就是我们目前所拥有的。不幸的是,这并不能为我们提供一种简单的方法来修改现有数据库-mysqldump生成的SQL脚本假定您是从头开始创建表的(如果存在,则覆盖表)。我们需要一些更高科技的东西,因为它需要对数据库应用一系列ALTER TABLE语句,并且要正确地执行此操作,它需要知道数据库的当前状态。
pix0r

5

如果您使用的是C#,请查看Subsonic,这是一个非常有用的ORM工具,但它也会生成sql脚本来重新创建您的方案和/或数据。然后可以将这些脚本放入源代码管理中。

http://subsonicproject.com/


截至目前,该网址似乎已失效。
Mark Schultheiss

5

我在Visual Studio中将以下数据库项目结构用于多个项目,并且效果很好:

数据库

变更脚本

0.PreDeploy.sql

1.SchemaChanges.sql

2.DataChanges.sql

3.Permissions.sql

创建脚本

Sprocs

功能

观看次数

然后,我们的构建系统通过按以下顺序执行脚本,将数据库从一个版本更新到另一个版本:

1.PreDeploy.sql

2.SchemaChanges.sql

“创建脚本”文件夹的内容

2.DataChanges.sql

3.Permissions.sql

每个开发人员通过将他们的代码附加到每个文件的末尾来检查他们对特定错误/功能的更改。完成主要版本并在源代码管理中分支后,更改脚本文件夹中的.sql文件的内容将被删除。


5

我们使用一个非常简单但有效的解决方案。

对于新安装,我们在存储库中有一个metadata.sql文件,其中包含所有数据库模式,然后在构建过程中,我们使用此文件来生成数据库。

对于更新,我们将更新添加到经过硬编码的软件中。我们将其保持为硬编码,因为我们不喜欢在问题真正成为问题之前就解决问题,而且到目前为止,这种事情尚未证明是问题。

因此,在我们的软件中,我们有以下内容:

RegisterUpgrade(1, 'ALTER TABLE XX ADD XY CHAR(1) NOT NULL;');

此代码将检查数据库是否为版本1(存储在自动创建的表中),如果数据库已过时,则执行该命令。

为了更新存储库中的metadata.sql,我们在本地运行此升级,然后提取完整的数据库元数据。

经常发生的唯一事情就是忘记提交metadata.sql,但这不是主要问题,因为它很容易在构建过程中进行测试,并且唯一可能发生的事情是使用进行新安装一个过时的数据库,并在首次使用时对其进行了升级。

同样,我们不支持降级,但是这是设计使然,如果更新中出现问题,我们将还原先前的版本并修复更新,然后再尝试。


4

我创建以构建版本命名的文件夹,然后在其中放置升级和降级脚本。例如,您可能具有以下文件夹:1.0.0、1.0.1和1.0.2。每个脚本都包含一个脚本,该脚本使您可以在两个版本之间升级或降级数据库。

如果客户或客户致电给您有关1.0.1版本的问题,而您正在使用1.0.2,则将数据库恢复到他的版本将不是问题。

在您的数据库中,创建一个名为“ schema”的表,在其中放置数据库的当前版本。然后,编写一个可以为您升级或降级数据库的程序很容易。

就像乔伊(Joey)所说的那样,如果您在Rails世界中,请使用Migrations。:)


3

对于我当前的PHP项目,我们使用Rails迁移的想法,并且有一个migrations目录,其中保留文件名为“ migration_XX.sql”的文件,其中XX是迁移的编号。当前,这些文件是在进行更新时手动创建的,但是可以轻松修改它们的创建。

然后,我们有一个名为“ Migration_watcher”的脚本,正如在pre-alpha中一样,该脚本当前在每次页面加载时运行,并检查是否存在新的migration_XX.sql文件,其中XX大于当前迁移版本。如果是这样,它将对数据库和所有数据库运行最大数量最大的所有migration_XX.sql文件!模式更改是自动的。

如果您需要还原系统的功能,则需要进行大量的调整,但这很简单,到目前为止,对于我们规模较小的团队来说,它一直运转良好。


3

我建议使用Ant(跨平台)作为“脚本”端(因为它实际上可以通过jdbc与那里的任何数据库对话)和Subversion作为源存储库。在进行更改之前,Ant会允许您将数据库“备份”到本地文件。1.通过Ant将现有的数据库模式备份到文件2.通过Ant将版本控制到Subversion存储库3.通过Ant将新的sql语句发送到db


3

Toad for MySQL具有一个称为架构比较的功能,该功能可让您同步2个数据库。到目前为止,这是我使用过的最好的工具。


3

我喜欢Yii处理数据库迁移的方式。迁移基本上是一个PHP脚本实现CDbMigrationCDbMigration定义一个up包含迁移逻辑的方法。也有可能实现一种down方法来支持迁移的逆转。或者,safeUpsafeDown可用于确保在事务上下文中完成迁移。

Yii的命令行工具yiic包含对创建和执行迁移的支持。可以逐个或批量应用或撤消迁移。创建迁移将导致实现PHP类的代码,该类CDbMigration基于时间戳和用户指定的迁移名称唯一地命名。先前已应用于数据库的所有迁移都存储在迁移表中。

有关更多信息,请参见手册中的“ 数据库迁移”文章。



2

恕我直言,迁移确实有一个巨大的问题:

从一个版本升级到另一个版本可以很好地工作,但是如果您有数百个表并且有很长的更改历史记录(像我们一样),那么全新安装给定版本可能会永远花费。

从基线到当前版本(对于数百个客户数据库)而言,运行增量的整个历史记录可能会花费很长时间。


0

有一个命令行mysql-diff工具可以比较数据库模式,其中模式可以是实时数据库或磁盘上的SQL脚本。它适合大多数架构迁移任务。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.