137

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

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

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

4

20 回答 20

57

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

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

这个 Oracle 的 Rails 迁移指南很好地涵盖了迁移。

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

于 2008-08-04T22:45:11.443 回答
50

我们使用类似于 bcwood 的东西来保持我们的数据库模式在 5 个不同的安装(生产、暂存和一些开发安装)之间保持同步,并在版本控制中进行备份,它运行良好。我会详细说明一下:


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

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

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

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

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

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


建立这个系统只花了几个小时,它在概念上很简单,每个人都得到了版本编号方案,它对于能够推进和改进数据库设计,而无需沟通或手动执行修改,这是非常宝贵的在所有数据库上。

不过,在粘贴来自 phpMyAdmin 的查询时要小心!这些生成的查询通常包括数据库名称,您绝对不想要它,因为它会破坏您的脚本!像 CREATE TABLE 这样的东西mydbnewtable(...) 如果系统上的数据库不称为 mydb,则会失败。我们创建了一个预先注释的 SVN 钩子,它将禁止包含该mydb字符串的 .sql 文件,这是一个明确的迹象,表明有人在没有适当检查的情况下从 phpMyAdmin 复制/粘贴。

于 2008-08-22T14:44:37.607 回答
12

我的团队编写了所有数据库更改的脚本,并将这些脚本连同应用程序的每个版本一起提交到 SVN。这允许对数据库进行增量更改,而不会丢失任何数据。

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

于 2008-08-05T19:56:43.217 回答
10

这里的问题实际上是让开发人员可以轻松地将自己的本地更改编写到源代码控制中以与团队共享。我多年来一直面临这个问题,并受到 Visual Studio for Database 专业人士的功能的启发。如果你想要一个具有相同功能的开源工具,试试这个:http ://dbsourcetools.codeplex.com/ 玩得开心,- Nathan。

于 2009-07-07T13:26:46.760 回答
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

于 2010-10-25T05:46:13.833 回答
8

Scott Ambler 撰写了一系列关于数据库重构的精彩文章(并与人合着了一本书),其理念是您应该本质上应用 TDD 原则和实践来维护您的模式。您为数据库设置了一系列结构和种子数据单元测试。然后,在您更改任何内容之前,您修改/编写测试以反映该更改。

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

事实证明,开发人员有时也会调整他们的沙箱数据库而忽略更新 SVN 中的模式文件。然后代码取决于尚未签入的数据库更改。这种错误可能非常难以确定,但测试套件会立即将其拾取。如果您将其内置到更大的持续集成计划中,这将特别好。

于 2008-08-29T04:51:30.517 回答
7

将架构转储到文件中并将其添加到源代码管理。然后一个简单的差异会告诉你发生了什么变化。

于 2008-08-06T16:59:36.803 回答
6

K. Scott Allen 有一两篇关于模式版本控制的不错的文章,它使用了此处其他答案中引用的增量更新脚本/迁移概念;请参阅http://odetocode.com/Blogs/scott/archive/2008/01/31/11710.aspx

于 2008-08-29T05:11:38.283 回答
5

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

然后,将脚本连同适用于它的代码一起提交到源代码管理中。当您需要随代码更改架构时,可以将脚本与需要更改架构的代码一起签入。然后,脚本上的差异将指示架构更改的差异。

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

于 2008-08-04T22:28:58.713 回答
5

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

http://subsonicproject.com/

于 2008-08-04T22:47:46.993 回答
5

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

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

对于更新,我们在硬编码的软件中添加更新。我们保持硬编码是因为我们不喜欢在问题真正成为问题之前解决问题,而这种事情到目前为止还没有被证明是一个问题。

所以在我们的软件中,我们有这样的东西:

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

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

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

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

此外,我们不支持降级,但这是设计使然,如果更新出现问题,我们会恢复以前的版本并修复更新,然后重试。

于 2008-08-08T18:21:33.993 回答
5

我在 Visual Studio 中为几个项目使用了以下数据库项目结构,并且效果很好:

数据库

更改脚本

0.PreDeploy.sql

1.SchemaChanges.sql

2.DataChanges.sql

3.权限.sql

创建脚本

存储过程

职能

意见

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

1.PreDeploy.sql

2.SchemaChanges.sql

创建脚本文件夹的内容

2.DataChanges.sql

3.权限.sql

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

于 2008-08-08T18:31:25.597 回答
4

我创建以构建版本命名的文件夹,并将升级和降级脚本放在那里。例如,您可能有以下文件夹:1.0.0、1.0.1 和 1.0.2。每个都包含允许您在版本之间升级或降级数据库的脚本。

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

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

就像 Joey 说的,如果你身处 Rails 世界,请使用 Migrations。:)

于 2008-08-05T04:36:13.340 回答
3

对于我当前的 PHP 项目,我们使用 rails 迁移的想法,并且我们有一个迁移目录,其中保存文件标题“migration_XX.sql”,其中 XX 是迁移的编号。目前,这些文件是在进行更新时手动创建的,但它们的创建可以很容易地修改。

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

如果您需要恢复系统的能力,则需要进行大量调整,但这很简单,并且到目前为止对于我们相当小的团队来说工作得很好。

于 2008-08-23T12:58:24.407 回答
3

我建议将 Ant(跨平台)用于“脚本”端(因为它实际上可以通过 jdbc 与任何数据库通信)和源存储库的 Subversion。Ant 将允许您在进行更改之前将您的数据库“备份”到本地文件。

  1. 通过 Ant 将现有的数据库模式备份到文件
  2. 通过 Ant 对 Subversion 存储库进行版本控制
  3. 通过 Ant 向 db 发送新的 sql 语句
于 2008-09-17T16:54:23.460 回答
3

Toad for MySQL 有一个称为模式比较的功能,可让您同步 2 个数据库。这是迄今为止我用过的最好的工具。

于 2011-02-05T11:08:58.650 回答
3

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

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

有关详细信息,请参阅手册中的数据库迁移文章。

于 2011-06-25T13:18:43.990 回答
3

尝试 db-deploy - 主要是一个 Java 工具,但也适用于 php。

于 2012-01-19T01:52:46.533 回答
2

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

从一个版本升级到另一个版本很好,但是如果您有数百个表和长期的更改历史(就像我们所做的那样),重新安装给定版本可能需要很长时间。

运行从基线到当前版本(数百个客户数据库)的增量历史可能需要很长时间。

于 2011-03-12T14:15:17.590 回答
0

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

于 2009-11-04T19:43:11.667 回答