如何在开发、测试和生产环境中管理数据库?

182

我一直很难找到管理开发、测试和生产服务器之间数据库架构和数据的良好示例。

我们的设置如下。每个开发人员都有一个虚拟机运行我们的应用程序和MySQL数据库,这是他们个人的沙盒,可以随意更改。目前,开发人员将对SQL架构进行更改,并将数据库转储为文本文件,然后提交到版本控制系统中。

我们想要部署一个持续集成开发服务器,它将始终运行最新提交的代码。如果现在这样做,每次构建都会从SVN重新加载数据库。

我们有一个运行“发布候选版”的测试(虚拟)服务器。部署到测试服务器目前是一个非常手动的过程,通常涉及我从SVN加载最新的SQL并进行微调。此外,测试服务器上的数据是不一致的。你最终会得到上一个提交者在他的沙盒服务器上拥有的任何测试数据。

所有问题的根源在于部署到生产环境。由于我们不能用测试数据覆盖实时数据,这就需要手动重新创建所有的架构更改。如果需要大量架构更改或转换脚本来操作数据,这可能会变得非常棘手。

如果问题仅限于架构,那将是一个更容易的问题,但是数据库中还有“基本”数据也在开发过程中更新,例如权限表中的元数据。

这是我看到实现持续集成和一步构建的最大障碍。你们怎么解决这个问题呢?


跟进问题:你如何跟踪数据库版本,以便知道要运行哪些脚本来升级给定的数据库实例?像Lance下面提到的版本表是标准程序吗?


谢谢你提供Tarantino的参考。虽然我不在.NET环境中,但我发现他们的DataBaseChangeMangement维基页面非常有帮助。特别是这个Powerpoint演示文稿(.ppt)

我将编写一个Python脚本,检查给定目录中的*.sql脚本的名称,并根据文件名的第一个整数部分顺序运行数据库中不存在的脚本。如果这是一个相当简单的解决方案,就像我预计的那样,我会在这里发布它。
我已经有一个可工作的脚本。它处理初始化数据库(如果不存在)和根据需要运行升级脚本。还有开关用于清除现有数据库以及从文件导入测试数据。代码大约有200行,所以我不会发布它(但如果有兴趣,我可能会把它放在pastebin上)。

相关:https://dev59.com/NHVD5IYBdhLWcg3wNIzc - Ashwin A
我将编写一个 Python 脚本,检查特定目录中的 *.sql 脚本的名称与数据库中的表相匹配,并按照文件名的第一部分构成的整数顺序运行不在其中的脚本。如果这是一个相当简单的解决方案,正如我所怀疑的那样,那么我将在此处发布它。听起来像你正在实现 flyway。 - masterxilo
14个回答

56
有几个不错的选择。我不会使用“恢复备份”策略。
  1. 编写所有模式更改脚本,并让CI服务器在数据库上运行这些脚本。拥有一个版本表来跟踪当前数据库版本,只有当它们适用于更新版本时才执行脚本。
  2. 使用迁移解决方案。这些解决方案因语言而异,但对于.NET,我使用Migrator.NET。这允许您对数据库进行版本控制,并在版本之间上下移动。您的模式是在C#代码中指定的。

29

您的开发人员需要为他们所开发的每个bug/功能编写更改脚本(模式和数据更改),而不仅仅是将整个数据库简单地转储到源代码控制中。这些脚本将把当前生产数据库升级到正在开发的新版本。

您的构建过程可以将生产数据库的副本还原到适当的环境中,并在其上运行来自源代码控制的所有脚本,从而将数据库更新到当前版本。我们每天都会执行此操作,以确保所有脚本都正确运行。


14

看一下Ruby on Rails是如何实现这个的。

首先有所谓的迁移文件,它们基本上将数据库模式和数据从版本N转换为版本N+1(或在降级从版本N+1到N的情况下)。数据库有一个表来告诉当前版本。

测试数据库总是在单元测试之前被清空,并用来自文件的固定数据进行填充。


11

这本书《重构数据库:进化式数据库设计》可能会给你一些管理数据库的思路。一份简短的版本也可以在http://martinfowler.com/articles/evodb.html阅读。

在一个PHP+MySQL项目中,我曾经将数据库修订号存储在数据库中,当程序连接到数据库时,它会首先检查修订号。如果程序需要不同的修订号,它将打开一个升级数据库的页面。每个升级都在PHP代码中指定,这将改变数据库架构并迁移所有现有数据。


6
  • 将数据库命名为如下格式 - dev_<<db>> , tst_<<db>> , stg_<<db>> , prd_<<db>>(显然,您永远不应该硬编码数据库名称
  • 这样,即使在相同的物理服务器上部署不同类型的数据库也是可行的(我不推荐这样做,但如果资源有限,您可能必须这样做)
  • 确保您能够自动在这些之间移动数据
  • 将数据库创建脚本与填充脚本分开 = 应始终可以从头重新创建数据库并填充它 (使用旧数据库版本或外部数据源)
  • 不要在代码中使用硬编码连接字符串(甚至不要在配置文件中)- 在配置文件中使用连接字符串模板,您可以动态填充每个应用程序层重新配置都需要重新编译是不好的
  • 使用数据库版本控制和数据库对象版本控制 - 如果您有能力使用现成产品,如果没有请自己开发
  • 跟踪每个DDL更改,并将其保存到某个历史记录表中 (示例在此)
  • 每天备份!测试您能够多快从备份中恢复丢失的内容(使用自动恢复脚本)
  • 即使您的DEV数据库和PROD具有完全相同的创建脚本,您仍将在数据方面遇到问题,因此允许开发人员创建与prod完全相同的副本并进行操作(我知道我会因为这个受到惩罚,但当出现问题时,思维方式和业务流程的变化成本要少得多 - 因此强制编码器合法订阅任何使它达到目标,但确保这一点

最后一点确实是情绪。如果必要的话,它显示项目的定义已经失效。开发必须领先于生产。如果生产数据引起副作用,那么就说明存在更大的问题。清理生产数据。此外,如果有理由 - 如您所建议的 - 必须将实时数据放在开发系统中,请与数据保护官员澄清最后一步,检查是否适用法律。此外,生产数据的精确副本会严重拖慢开发和集成速度。如果您负担不起这样的奢侈品,请考虑使用成本更低的流程。 - hakre
问题在于,在开发过程中根本无法预见控制流程中的所有边角情况和生产中会发生的数据质量变化。如果您在这样一个大公司中,为此而面临法律问题,那么必须实施某种数据混淆和/或屏蔽解决方案,这增加了额外的复杂性,但仍必须保留导致错误的数据质量方面... - Yordan Georgiev

5
你可以考虑使用类似于SQL Compare这样的工具,来脚本化不同版本数据库之间的差异,从而快速迁移版本。

3

恐怕我同意其他帖子的观点。开发人员需要编写脚本来进行更改。

在许多情况下,简单的ALTER TABLE是不起作用的,您还需要修改现有数据 - 开发人员需要考虑需要进行哪些迁移并确保它们被正确地编写了脚本(当然,您需要在发布周期的某个时候仔细测试这一点)。

此外,如果您有任何常识,您将让您的开发人员为其更改编写回滚脚本,以便在需要时可以撤消更改。这也应该经过测试,以确保它们的回滚不仅可以无错误执行,而且将数据库恢复到之前的状态(这并非总是可能或可取,但大多数情况下都是一个好的规则)。

我不知道如何将其与CI服务器连接起来。也许您的CI服务器需要具有已知的构建快照,每晚都会还原到该快照,然后应用自那时以来的所有更改。这可能是最好的方法,否则损坏的迁移脚本将不仅破坏那天晚上的构建,而且破坏所有随后的构建。


3

这是我一直不满意的事情 - 我们对这个问题的解决方案。几年来,我们为每个发布版本都维护一个单独的变更脚本。此脚本将包含自上一个生产版本以来的差异。随着应用程序的每次发布,版本号将递增,得到以下内容:

  • dbChanges_1.sql
  • dbChanges_2.sql
  • ...
  • dbChanges_n.sql

这个方法在我们只有一条开发线时表现良好:Trunk/Mainline用于新开发,维护分支用于修复错误、短期增强等。然而,当需要在分支中更改模式时,问题就来了。此时,我们已经在Trunk中拥有了dbChanges_n+1.sql,因此我们最终采取了以下方案:

  • dbChanges_n.1.sql
  • dbChanges_n.2.sql
  • ...
  • dbChanges_n.3.sql

之前这种做法还算有效,直到有一天我们抬头看到主干中有42个增量脚本,在分支中有10个。烦死了!

现在我们只维护一个增量脚本,并让SVN对其进行版本控制——即每次发布时都会覆盖脚本。而且我们避免在分支中进行模式更改。

所以,我对此也不满意。我非常喜欢Rails中的迁移概念。我已经对LiquiBase产生了浓厚的兴趣。它支持增量数据库重构的概念。值得一看,我很快就会详细研究它。有没有人有使用它的经验?我很想听听你的结果。


3
我们的设置与OP非常相似。
开发人员在私有数据库中的虚拟机中进行开发。
[开发人员很快将提交到私有分支]
测试在不同的机器上运行(实际上在服务器上托管的VM中运行) [很快将由Hudson CI服务器运行]
通过将参考转储加载到数据库中进行测试。 应用开发人员模式补丁,然后应用开发人员数据补丁
然后运行单元测试和系统测试。
生产环境以安装程序形式部署给客户。
我们的操作:
我们对沙盒数据库进行模式转储。 然后是SQL数据转储。 我们将其与先前的基线进行比较。
这一对差异用于将n-1升级到n。
我们配置转储和差异。
因此,要安装版本N CLEAN,我们需要将dump运��到空数据库中。 要打补丁,请应用介入的补丁。
(Juha提到Rails的想法记录当前DB版本的表是一个好主意,并应该使安装更新更加轻松。)
必须在beta测试之前审核差异和转储。 我看不出有任何绕过此问题的方法,因为我已经看到开发人员向数据库插入测试帐户。

1

我们正在使用命令行mysql-diff:它会输出两个数据库模式(从实时数据库或脚本)之间的差异作为ALTER脚本。mysql-diff在应用程序启动时执行,如果模式发生更改,则向开发人员报告。因此,开发人员无需手动编写ALTER,模式更新会半自动化地进行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接