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


103

在SO社区Wiki上已经进行了一些讨论,即是否应该对数据库对象进行版本控制。但是,关于创建数据库对象的构建自动化过程的最佳实践我还没有看到太多讨论。

对于我的团队来说,这一直是一个有争议的讨论点,尤其是因为在评估自动化数据库部署方法的收益和风险时,开发人员和DBA通常具有不同的目标,方法和关注点。

我想听听SO社区的一些想法,这些想法在现实世界中是有效的。

我意识到哪种做法​​真正最好是有些主观的,但是我认为就什么工作进行很好的对话可能对许多人有帮助。

这是我对本主题关注领域的一些预告问题。这些并不是确定的清单,而是帮助人们了解我在寻找什么的起点。

  1. 测试和生产环境都应该从源代码控制构建吗?
    • 两者都应该使用自动化来构建-还是应该通过从稳定的最终测试环境中复制对象来构建产品?
    • 您如何处理部署脚本中测试和生产环境之间的潜在差异?
    • 您如何测试部署脚本能否像测试中那样有效地针对生产工作?
  2. 哪些类型的对象应进行版本控制?
    • 只是代码(过程,包,触发器,java等)?
    • 索引?
    • 约束?
    • 表定义?
    • 表更改脚本?(例如ALTER脚本)
    • 一切?
  3. 哪些类型的对象不应该进行版本控制?
    • 顺序?
    • 补助金?
    • 用户帐号?
  4. 在SCM存储库中应如何组织数据库对象?
    • 您如何处理一次性脚本,例如转换脚本或ALTER脚本?
    • 您如何处理从数据库中退出的对象?
    • 谁应该负责对象从开发提升到测试级别?
    • 您如何协调来自多个开发人员的更改?
    • 您如何处理多个系统使用的数据库对象的分支?
  5. 可以合理地对此过程进行哪些例外(如果有)?
    • 安全问题?
    • 数据是否涉及身份识别问题?
    • 不能完全自动化的脚本?
  6. 您如何使流程具有弹性和可执行性?
    • 给开发人员报错?
    • 遇到意外的环境问题?
    • 为了灾难恢复?
  7. 您如何使决策者相信DB-SCM的优势确实证明了成本合理?
    • 传闻?
    • 行业研究?
    • 行业最佳实践建议?
    • 呼吁公认的当局?
    • 成本效益分析?
  8. 在此模型中,谁应该“拥有”数据库对象?
    • 开发人员?
    • DBA?
    • 数据分析师?
    • 超过一个?

3
这个问题的深度要求悬赏。
格雷格D

Answers:


53

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

  1. 测试和生产环境都应该从源代码控制构建吗?
    • 两者都应该使用自动化来构建-还是应该通过从稳定的最终测试环境中复制对象来构建产品?
    • 两者的自动化。不要在环境之间复制数据
    • 您如何处理部署脚本中测试和生产环境之间的潜在差异?
    • 使用模板,以便实际上您将为每个环境生成不同的脚本集(例如,对外部系统,链接数据库的引用等)
    • 您如何测试部署脚本能否像测试中那样有效地针对生产工作?
    • 您在生产前环境中对其进行测试:在生产环境(数据库和其他潜在系统)的精确副本上测试部署
  2. 哪些类型的对象应进行版本控制?
    • 只是代码(过程,包,触发器,java等)?
    • 索引?
    • 约束?
    • 表定义?
    • 表更改脚本?(例如ALTER脚本)
    • 一切?
    • 一切,以及:
      • 不要忘记静态数据(查找列表等),因此您无需在环境之间复制任何数据
      • 仅保留当前版本的数据库脚本(当然是版本控制的),并且
      • 存储ALTER脚本:1个BIG脚本(或名为like_001_AlterXXX.sql的脚本目录,以便以自然排序顺序运行它们将从版本A升级到版本B)
  3. 哪些类型的对象不应该进行版本控制?
    • 顺序?
    • 补助金?
    • 用户帐号?
    • 请参阅2.如果您的用户/角色(或技术用户名)在环境之间不同,您仍然可以使用模板编写脚本(请参阅1.)。
  4. 在SCM存储库中应如何组织数据库对象?
    • 您如何处理一次性脚本,例如转换脚本或ALTER脚本?
    • 见2。
    • 您如何处理从数据库中退出的对象?
    • 从数据库中删除,从源代码控制干线/提示中删除
    • 谁应该负责将对象从开发提升到测试级别?
    • 开发/测试/发布时间表
    • 您如何协调来自多个开发人员的更改?
    • 尽量不要为每个开发人员创建一个单独的数据库。您使用源代码控制,对不对?在这种情况下,开发人员将更改数据库并签入脚本。为了完全安全,请在每晚构建时从脚本重新创建数据库
    • 您如何处理多个系统使用的数据库对象的分支?
    • 强硬的一:尽一切可能避免。
  5. 可以合理地对此过程进行哪些例外(如果有)?
    • 安全问题?
    • 不要存储测试/产品的密码。您可以允许开发人员使用它,特别是如果您每天/每晚都有自动数据库重建
    • 数据是否涉及身份识别问题?
    • 不能完全自动化的脚本?
    • 文档并使用发布信息/ ALTER脚本进行存储
  6. 您如何使流程具有弹性和可执行性?
    • 给开发人员报错?
    • 从头开始进行每日构建测试,并将结果与​​增量升级(使用ALTER从A版本升级到B版本)进行比较。比较结果模式和静态数据
    • 遇到意外的环境问题?
    • 使用版本控制和备份
    • 比较PROD数据库架构与您的想法,尤其是在部署之前。SuperDuperCool DBA可能已修复票务系统中从未存在的错误:)
    • 为了灾难恢复?
  7. 您如何使决策者相信DB-SCM的优势确实证明了成本合理?
    • 传闻?
    • 行业研究?
    • 行业最佳实践建议?
    • 呼吁公认的当局?
    • 成本效益分析?
    • 我认为,如果开发人员和DBA同意,则您无需说服任何人(除非您需要花钱购买用于MSSQL 的dbGhost之类的软件)
  8. 在此模型中,谁应该“拥有”数据库对象?
    • 开发人员?
    • DBA?
    • 数据分析师?
    • 超过一个?
    • 通常,DBA会批准模型(在签入之前或在代码审查的一部分之后)。他们绝对拥有与性能相关的对象。但是总的来说,团队拥有它[当然,还有雇主:)

感谢您的回答!您认为这些建议适用于所有项目吗?您是否知道任何有助于实现这种自动化水平的工具?随着更多人参与其中,我将更新我的问题。另外,请注意,我的“挑逗”问题并不是要解决的明确问题列表,而是作为讨论的起点。
LBushkin

明确。我认为您提出了一个非常非常好的问题。我真的希望这个问题能引起您的广泛关注,以便您编写有关该主题的出色的HowTo Wiki。----从工具开始:我使用了Innovartis的dbGhost,我在答案中提到了管理MSSQL服务器,它做得很好。可能还有其他工具可以完成这项工作,但是鉴于完全的SQL模式并不是供应商之间真正的标准,因此没有针对所有SCM和RDMBS的一体化解决方案。
van van

好答案。我假设“查找列表”是指用于填充<select>框的数据?这可能并不总是可能的,因为用户可能会在生产中修改数据(以某种方式)。在这种情况下,保留备份很有意义。您还建议作为每晚构建的一部分从头开始重新创建数据库。我认为那不是一个好主意。它可以删除进行中的工作,或要求重新安装/配置其他软件。最后,我建议创建测试模式,数据验证器和其他工具,而不是从头开始构建以确保弹性过程。
理查德·勒瓦瑟

@理查德。好点,但仍然。当您阅读“从头开始构建数据库”时,并不总是意味着删除数据。它是重建架构和静态数据。如果有一些正在进行的工作(关于模式或静态数据),则应将其放在源代码管理中,因此它将成为夜间构建的一部分。如果您在分支上进行主要的数据库重新设计,则将有一个单独的数据库来进行这种开发。而且,如果您使用单元测试,那么您将不依赖数据库中的数据状态,而是将其创建/删除作为db-unit-test的一部分。---用户消耗的静态数据-同意。
van van

我不同意每晚重建数据库。虽然定义数据库的所有内容(例如模式,触发器,存储过程和某些静态“托管”数据)都应编写脚本,但实际材料不应该编写脚本。内容本身可能受开发人员任务的驱动。我们有一些开发人员致力于数据集的测试和实验。如果他们每天都进来并且当前状态为“重置”怎么办?不好。我使用TestNG测试来擦除某些表,然后每天预先加载测试数据。听起来不像是源代码管理的候选者。
gregturn

5

我尽可能将SQL视为源代码

如果我可以用符合标准的SQL编写它,那么它通常放在源代码管理的文件中。该文件将尽可能定义诸如SP,Table CREATE语句之类的内容。

我还将在源代码管理中包含用于测试的伪数据:

  1. proj / sql / setup_db.sql
  2. proj / sql / dummy_data.sql
  3. proj / sql / mssql_specific.sql
  4. proj / sql / mysql_specific.sql

然后,我抽象出所有SQL查询,以便可以为MySQL,Oracle,MSSQL或其他任何对象构建整个项目。

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


4

我们通过TeamCity使用持续集成。在到源代码管理的每个签入中,从头开始重新构建数据库和所有测试数据,然后重新构建代码,然后针对该代码运行单元测试。如果您使用的是CodeSmith之类的代码生成工具,则也可以将其放置在构建过程中,以在每次构建时都重新生成数据访问层,确保所有层“匹配”并且不会由于以下原因而产生错误SP参数不匹配或缺少列。

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

根据查找表,我们的大多数查找值也存储在脚本中,并运行以确保配置数据符合我们的期望,例如“ reason_codes”或“ country_codes”。这样,我们可以在开发人员中更改查找数据,进行测试,然后通过质量检查和生产“推广”它,而不是使用工具来修改生产中的查找值,这对正常运行很危险。

我们还会创建一组“回滚”脚本,以撤消我们对数据库的更改,以防生产环境出现问题。您可以通过运行回滚脚本来对其进行测试,然后在其部署脚本运行之后重新运行低于您的版本的版本的单元测试。


4

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

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

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

值得一提的是,LiquiBase并未集成到广泛的SQL IDE中,您应该自己进行基本操作。

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

恕我直言:

  1. 将DML存储在文件中,以便您可以对其进行版本控制。
  2. 通过源代码管理自动化架构构建过程。
  3. 为了进行测试,开发人员可以使用从源代码控制通过构建系统+负载测试通过脚本或DBUnit脚本(从源代码控制)构建的本地数据库。
  4. LiquiBase允许您提供脚本的“运行顺序”以遵守相关性。
  5. 应该有DBA团队在生产使用前检查所有所有更改的主早午餐。我的意思是,他们在提交到MASTER干线之前先检查其他DBA的干线/分支。因此,该母版始终保持一致并且可以进行生产。

我们在计费生产数据库中遇到了代码更改,合并,重写等所有提到的问题。本主题非常适合发现所有这些东西。


3

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


我认为不可能达成一个普遍接受的答案。但是,我想指出一些共识的共同领域-也许会提出一个合理的建议。当然,我也一定会看一下agileDatabases论坛。
LBushkin,2009年

3

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

  • 始终可以通过启动一个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中,方案也是用户,这经常发生。


1

我们在Git版本控制中拥有带有MSSQL数据库的Silverlight项目。最简单的方法是确保您拥有精简的数据库(内容明智),并从fe Visual Studio 进行完整的转储。然后,您可以从构建脚本中执行“ sqlcmd”,以在每台开发机上重新创建数据库。

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


1

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

这并不适合所有人。

这并不适用于每个项目。我通常在绿色领域的项目上工作,这给我带来了方便!


很高兴听到您在数据库开发生命周期中使用SQL Compare。我们认为我们可能已经提高了开发人员进行新更改的难度。检出SQL Source Control。它与SQL Compare并存,以简化开发人员的协作,并允许您保持架构对象的源代码受控(前提是您使用的是SVN或TFS)。red-gate.com/products/sql_source_control/index.htm
David Atkinson,2010年

1

这不是解决白塔争论的问题,而是一种解决方案,对我在现实世界中的问题非常有效。

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

DBdeploy是一个工具,它将检查数据库的当前状态-例如,以前已针对该数据库运行了哪些脚本,可以运行哪些脚本,因此需要运行哪些脚本。

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

它不是最漂亮的工具,也不是最复杂的工具,但经过精心的管理,它可以很好地工作。它是开源的,易于扩展。一旦很好地处理了脚本的运行,就可以轻松添加一些额外的组件,例如shell脚本,该脚本签出最新的脚本并针对特定实例运行dbdeploy。

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

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



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.