你应该如何从源代码控制构建数据库?

时间:2009-06-12 18:59:51

标签: database language-agnostic version-control

关于数据库对象是否应该受版本控制的SO社区wiki已经有一些讨论。但是,我没有看到很多关于为数据库对象创建构建自动化过程的最佳实践的讨论。

这对我的团队来说是一个有争议的讨论点 - 特别是在评估数据库部署自动化方法的好处和风险时,开发人员和DBA经常有不同的目标,方法和关注点。

我想听听SO社区关于哪些实践在现实世界中有效的一些想法。

我意识到这有点主观,哪种做法真的是最好的,但我认为一个关于哪些工作可能对许多人有帮助的良好对话。

以下是关于本主题所关注领域的一些预告片问题。这些并不是一个明确的清单 - 而是人们帮助理解我正在寻找的东西的起点。

  1. 测试和生产环境是否应该从源代码控制构建?
    • 是否应该使用自动化构建 - 或者应该通过从稳定的,最终的测试环境中复制对象来构建生产?
    • 您如何处理部署脚本中测试和生产环境之间的潜在差异?
    • 如何测试部署脚本是否可以像在测试中那样有效地对生产工作?
  2. 哪些类型的对象应该受版本控制?
    • 只是代码(程序,包,触发器,java等)?
    • 索引?
    • 约束?
    • 表定义?
    • 表格更改脚本? (例如,ALTER脚本)
    • 一切?
  3. 哪些类型的对象不应受版本控制?
    • 序列?
    • 奖励?
    • 用户帐户?
  4. 如何在SCM存储库中组织数据库对象?
    • 您如何处理转换脚本或ALTER脚本等一次性内容?
    • 您如何处理退出数据库中的对象?
    • 谁应该负责对象从开发升级到测试级别?
    • 如何协调来自多个开发人员的更改?
    • 如何处理多个系统使用的数据库对象的分支?
  5. 对此流程可以合理地做出哪些例外(如果有的话)?
    • 安全问题?
    • 具有去识别问题的数据?
    • 无法完全自动化的脚本?
  6. 如何使流程具有弹性和可执行性?
    • 开发人员错误?
    • 出乎意料的环境问题?
    • 用于灾难恢复?
  7. 您如何让决策者相信DB-SCM的好处才能真正证明成本合理?
    • 轶事证据?
    • 行业研究?
    • 行业最佳实践建议?
    • 向公认的当局提出上诉?
    • 成本/效益分析?
  8. 谁应该“拥有”此模型中的数据库对象?
    • 开发?
    • 的DBA?
    • 数据分析师?
    • 不止一个?

11 个答案:

答案 0 :(得分:53)

以下是您的问题的一些答案:

  1. 测试和生产环境是否应该从源代码控制构建?
    • 是否应该使用自动化构建 - 或者应该通过从稳定的,最终的测试环境中复制对象来构建生产?
    • 两者的自动化。请勿在环境之间复制数据
    • 您如何处理部署脚本中测试和生产环境之间的潜在差异?
    • 使用模板,这样实际上您可以为每个环境生成不同的脚本集(例如,对外部系统,链接数据库等的引用)
    • 如何测试部署脚本是否可以像在测试中那样有效地对生产工作?
    • 您在预生产环境中测试它们:在生产环境的精确副本(数据库和可能的其他系统)上测试部署
  2. 哪些类型的对象应该受版本控制?
    • 只是代码(程序,包,触发器,java等)?
    • 索引?
    • 约束?
    • 表定义?
    • 表格更改脚本? (例如,ALTER脚本)
    • 一切?
    • 一切,并且:
      • 不要忘记静态数据(查找列表等),因此您无需在环境之间复制任何数据
      • 仅保留当前版本的数据库脚本(当然是受版本控制)和
      • 存储ALTER脚本:1个BIG脚本(或名为001_AlterXXX.sql的脚本目录,因此以自然排序顺序运行它们将从A版升级到B)
  3. 哪些类型的对象不应受版本控制?
    • 序列?
    • 奖励?
    • 用户帐户?
    • 请参阅2.如果您的用户/角色(或技术用户名)在不同环境之间有所不同,您仍然可以使用模板编写脚本(请参阅1.)
  4. 如何在SCM存储库中组织数据库对象?
    • 您如何处理转换脚本或ALTER脚本等一次性内容?
    • 见2。
    • 您如何处理退出数据库中的对象?
    • 已从数据库中删除,已从源代码管理中继/提示中删除
    • 谁应该负责将对象从开发推广到测试级别?
    • 开发/测试/发布时间表
    • 如何协调来自多个开发人员的更改?
    • 尽量不为每个开发人员创建单独的数据库。你使用源代码控制,对吗?在这种情况下,开发人员更改数据库并签入脚本。为了完全安全,在夜间构建期间从脚本重新创建数据库
    • 如何处理多个系统使用的数据库对象的分支?
    • 艰难的:尽量避免不惜一切代价。
  5. 对此流程可以合理地做出哪些例外(如果有的话)?
    • 安全问题?
    • 不存储test / prod的密码。你可以允许它开发,特别是如果你有自动每日/每晚数据库重建
    • 具有去识别问题的数据?
    • 无法完全自动化的脚本?
    • 使用发布信息/ ALTER脚本进行文档和商店
  6. 如何使流程具有弹性和可执行性?
    • 开发人员错误?
    • 从头开始每日构建测试,并将结果与​​增量升级进行比较(使用ALTER从版本A到B)。比较结果架构和静态数据
    • 出乎意料的环境问题?
    • 使用版本控制和备份
    • 将PROD数据库架构与您的想法进行比较,尤其是在部署之前。 SuperDuperCool DBA可能修复了票证系统中从未出现过的错误:)
    • 用于灾难恢复?
  7. 您如何让决策者相信DB-SCM的好处才能真正证明成本合理?
    • 轶事证据?
    • 行业研究?
    • 行业最佳实践建议?
    • 向公认的当局提出上诉?
    • 成本/效益分析?
    • 如果开发人员和DBA同意,您不需要说服任何人,我认为(除非您需要钱购买MS dbGhost之类的软件)
  8. 谁应该“拥有”此模型中的数据库对象?
    • 开发?
    • 的DBA?
    • 数据分析师?
    • 不止一个?
    • 通常,DBA会批准该模型(在办理登机手续之前或之后作为代码审查的一部分)。他们肯定拥有与性能相关的对象但总的来说,团队拥有它[和雇主,当然:)]

答案 1 :(得分:5)

我尽可能将SQL视为源代码

如果我可以在标准兼容的SQL 中编写它,那么它通常会在我的源代码管理中的文件中。该文件将尽可能多地定义,例如SP,表CREATE语句。

我还包括用于在源代码管理中进行测试的虚拟数据:

  1. 凸出/ SQL / setup_db.sql
  2. 凸出/ SQL / dummy_data.sql
  3. 凸出/ SQL / mssql_specific.sql
  4. 凸出/ SQL / mysql_specific.sql
  5. 然后我抽象出所有的SQL查询,这样我就可以为MySQL,Oracle,MSSQL或其他任何东西构建整个项目。

    构建和测试自动化使用这些构建脚本作为它们与应用程序源一样重要,并测试从完整性到触发器,过程和日志记录的所有内容。

答案 2 :(得分:4)

我们通过TeamCity使用持续集成。在每次签入源代码控制时,从头开始重新构建数据库和所有测试数据,然后是代码,然后针对代码运行单元测试。如果您正在使用像CodeSmith这样的代码生成工具,它也可以放入您的构建过程中,以便在每次构建时生成新的数据访问层,确保所有层“匹配”并且不会因为SP参数不匹配或缺少列。

每个构建都有自己的SQL脚本集合,这些脚本存储在源代码管理的$ project \ SQL \目录中,分配了一个数字前缀并按顺序执行。这样,我们就会在每次构建时练习部署过程。

根据查找表,我们的大多数查找值也存储在脚本中并运行以确保配置数据符合我们的预期,例如“reason_codes”或“country_codes”。通过这种方式,我们可以在开发中进行查找数据更改,测试它,然后通过QA和生产“提升”它,而不是使用工具来修改生产中的查找值,这对正常运行时间来说是危险的。

我们还创建了一组“回滚”脚本来撤消我们的数据库更改,以防生产构建变得棘手。您可以通过运行它们来测试回滚脚本,然后在部署脚本运行后重新运行您下面的构建版本的单元测试。

答案 3 :(得分:4)

Liquibase

+1: LiquiBase是一个开源(LGPL),独立于数据库的库,用于跟踪,管理和应用数据库更改。它建立在一个简单的前提上:所有数据库更改(结构和数据)都以基于XML的描述方式存储,并检入源代码控制。 好的一点是,DML更改是在语义上存储的,而不仅仅是diff,因此您可以跟踪更改的目的。

它可以与GIT版本控制结合使用,以实现更好的互动。我将配置我们的dev-prod环境来试用它。

此外,您可以使用 Maven,Ant 构建系统从脚本构建生产代码。

Tha减去是LiquiBase没有集成到广泛的SQL IDE中,你应该自己做基本的操作。

除此之外,您还可以使用DBUnit进行数据库测试 - 此工具允许使用数据生成脚本来测试您的生产环境并进行清理。

IMHO:

  1. 将DML存储在文件中,以便您可以 他们的版本。
  2. 自动化架构构建过程 源控制。
  3. 出于测试目的,开发人员可以 使用从中构建的本地数据库     通过构建系统+来源控制     使用脚本加载测试数据,或     DBUnit脚本(来自Source     控制)。
  4. LiquiBase允许您提供“运行 序列“尊重的脚本     依赖性。
  5. 应该有DBA团队检查主人 所有变化的早午餐     在生产使用之前。我的意思是他们     检查来自其他DBA的trunk / branch     在进入MASTER主干之前。     所以主人总是一致的     并准备好生产。
  6. 我们在计费生产数据库中遇到了所有提到的代码更改,合并和重写问题。这个主题非常适合发现所有这些内容。

答案 4 :(得分:3)

通过询问“预告片问题”,您似乎对某个讨论更感兴趣,而不是某人对最终答案的看法。活跃的(> 2500名成员)邮件列表agileDatabases解决了许多这些问题,根据我的经验,这是一个用于此类讨论的复杂而民用的论坛。

答案 5 :(得分:3)

我基本同意van给出的每个答案。更深入的了解,我的数据库管理基准是K. Scott Allen series(必读,恕我直言。而Jeff's opinion似乎也是如此)。

  • 通过启动单个SQL文件(本身可以调用其他SQL文件),始终可以从头开始重建数据库对象:Create.sql。这可以包括静态数据插入(列表......)。
  • SQL脚本已参数化,因此不会在纯文件中存储依赖于环境的和/或敏感信息。
  • 我使用自定义批处理文件来启动Create.sqlCreate.cmd。它的目标主要是检查先决条件(工具,环境变量......)并将参数发送到SQL脚本。它还可以从CSV文件批量加载静态数据以解决性能问题。
  • 通常,系统用户凭据将作为参数传递给Create.cmd文件。

恕我直言,动态数据加载应该需要另一个步骤,具体取决于您的环境。开发人员希望根据测试,垃圾或无数据加载他们的数据库,而在另一端,生产经理将希望加载生产数据。我也会考虑将测试数据存储在源代码控制中(例如,为了简化单元测试)。

一旦数据库的第一个版本投入生产,您不仅需要构建脚本(主要面向开发人员),还需要升级脚本(基于相同的原则):

  • 必须有一种从数据库中检索版本的方法(我使用存储过程,但表也可以。)
  • 在发布新版本之前,我创建了一个Upgrade.sql文件(可以调用其他版本),允许将版本N-1升级到版本N(N是正在发布的版本)。我将此脚本存储在名为N-1
  • 的文件夹下
  • 我有一个进行升级的批处理文件:Upgrade.cmd。它可以通过简单的SELECT语句检索数据库的当前版本(CV),启动存储在Upgrade.sql文件夹下的CV脚本,然后循环直到找不到文件夹。这样,您就可以自动从N-3升级到N.

问题是:

  • 很难自动比较数据库模式,具体取决于数据库供应商。这可能导致升级脚本不完整。
  • 对生产环境的每次更改(通常由DBA进行性能调整)也应该找到源代码控制的方式。为了确保这一点,通常可以通过触发器记录对数据库的每个修改。每次升级后都会重置此日志。
  • 更理想的是,DBA发起的更改应尽可能是发布/升级过程的一部分。

您希望在源代码管理下拥有哪种数据库对象?好吧,我会尽可能地说,但不是更多;-)如果你想用密码创建用户,给他们一个默认密码(登录/登录,实用于单元测试目的),并使密码更改为手动操作。这种情况在Oracle中发生很多,其中模式也是用户......

答案 6 :(得分:1)

我们在Git版本控制中使用带有MSSQL数据库的Silverlight项目。最简单的方法是确保你有一个精简的数据库(内容明智),并从f.e.做一个完整的转储。视觉工作室。然后,您可以从构建脚本执行'sqlcmd',以在每台开发机器上重新创建数据库。

对于部署,这是不可能的,因为数据库太大:这是将它们放在数据库中的主要原因。

答案 7 :(得分:1)

我坚信DB应该是源代码控制的一部分,并且在很大程度上是构建过程的一部分。如果它在源代码控制中,那么在SQL中编写存储过程时,我就像在C#中编写类时一样,具有相同的编码安全保护。我通过在源代码树下包含一个DB脚本目录来完成此操作。此脚本目录不一定有一个文件用于数据库中的一个对象。这将是一个痛苦的屁股!我在我的数据库中开发只是我想在我的代码项目中。然后当我准备好检查时,我在我的数据库的最后一个版本和我正在处理的当前版本之间做了一个差异。我为此使用SQL Compare,它会生成所有更改的脚本。然后将此脚本保存到我的db_update目录,其中包含特定的命名约定1234_TasksCompletedInThisIteration,其中该数字是已存在的脚本集中的下一个数字,名称描述了此检查中正在执行的操作。我这样做是因为as我的构建过程的一部分,我从一个新的数据库开始,然后使用此目录中的脚本以编程方式构建。我编写了一个自定义的NAnt任务,它遍历在裸db上执行其内容的每个脚本。显然,如果我需要一些数据进入数据库,那么我也有数据插入脚本。这也有很多好处。一,我所有的东西都是版本的。二,每个构建都是一个新构建,这意味着不会有任何偷偷摸摸的东西进入我的开发过程(例如导致系统奇怪的脏数据)。三,当一个新人被添加到开发团队时,他们只需要获得最新信息,他们的本地开发人员即可为他们构建。四,我可以在我的数据库上运行测试用例(我没有把它称为“单元测试”!),因为每次构建都会重置数据库的状态(这意味着我可以测试我的存储库而不必担心将测试数据添加到分贝)。

这不适合所有人。

这不适用于每个项目。我经常在绿色的田野项目上工作,这让我很方便!

答案 8 :(得分:1)

这不是进入白塔的论点,而是一个对我来说非常适合现实世界问题的解决方案。

从头开始构建数据库可以概括为管理sql脚本。

DBdeploy 是一种检查数据库当前状态的工具 - 例如以前运行过哪些脚本,可以运行哪些脚本,因此需要运行哪些脚本。

然后它将整理所有需要的脚本并运行它们。然后它记录已运行的脚本。

这不是最漂亮的工具或最复杂的工具 - 但仔细管理它可以很好地工作。它是开源的,易于扩展。一旦很好地处理了脚本的运行,就可以很容易地添加一些额外的组件,例如一个shell脚本,它可以检查最新的脚本,并且可以轻松地针对特定实例运行dbdeploy。

在这里看到一个很好的介绍:

http://code.google.com/p/dbdeploy/wiki/GettingStarted

答案 9 :(得分:0)

您可能会发现Liquibase处理了很多您正在寻找的内容。

答案 10 :(得分:0)

每个开发人员都应拥有自己的本地数据库,并使用源代码控制发布到团队。我的解决方案在这里:http://dbsourcetools.codeplex.com/ 玩得开心, - 内森