你应该如何从源代码控制build立你的数据库?

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

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

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

我意识到这种做法是最好的,但是我认为,对于许多人来说,哪些工作会有所帮助,是一个很好的对话。

这里有一些关于这个主题关注领域的传情问题。 这些并不意味着是一个明确的清单 – 而是帮助人们理解我所寻找的东西的起点。

  1. testing和生产环境是否应该从源代码pipe理构build?
    • 是否应该使用自动化来构build – 还是应该通过从稳定的最终testing环境复制对象来生产?
    • 如何处理部署脚本中的testing和生产环境之间的潜在差异?
    • 您如何testing部署脚本将像testing中那样有效地对抗生产?
  2. 什么types的对象应该版本控制?
    • 只是代码(程序,软件包,触发器,Java等)?
    • 指标?
    • 约束?
    • 表定义?
    • 表更改脚本? (如ALTER脚本)
    • 一切?
  3. 哪些types的对象不应该受版本控制?
    • 序列?
    • 资助?
    • 用户帐户?
  4. 数据库对象应该如何在您的SCM存储库中进行组织?
    • 你如何处理像转换脚本或ALTER脚本一次性的东西?
    • 你如何处理来自数据库的退休对象?
    • 谁应该负责推动对象从开发到testing水平?
    • 你如何协调来自多个开发人员的变化?
    • 你如何处理多个系统使用的数据库对象的分支?
  5. 如果有的话,有什么例外可以合理地做到这一过程?
    • 安全问题?
    • 数据与去识别的关注?
    • 脚本不能完全自动化?
  6. 你如何使这个过程具有弹性和可执行性?
    • 要开发人员错误?
    • 意想不到的环境问题?
    • 为了灾难恢复?
  7. 你如何说服决策者,DB-SCM的好处真正certificate了成本?
    • 传闻?
    • 行业研究?
    • 行业最佳实践build议?
    • 呼吁公认的权威机构?
    • 成本效益分析?
  8. 谁应该在这个模型中“拥有”数据库对象?
    • 开发商?
    • 数据库pipe理员?
    • 数据分析师?
    • 超过一个?

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

  1. testing和生产环境是否应该从源代码pipe理构build?
    • 是否应该使用自动化来构build – 还是应该通过从稳定的最终testing环境复制对象来生产?
    • 两者的自动化。 不要在环境之间复制数据
    • 如何处理部署脚本中的testing和生产环境之间的潜在差异?
    • 使用模板,实际上你会为每个环境产生不同的脚本集(例如引用外部系统,链接数据库等)
    • 您如何testing部署脚本将像testing中那样有效地对抗生产?
    • 您可以在预生产环境中对其进行testing:在生产环境的准确副本(数据库和潜在的其他系统)上testing部署,
  2. 什么types的对象应该版本控制?
    • 只是代码(程序,软件包,触发器,Java等)?
    • 指标?
    • 约束?
    • 表定义?
    • 表更改脚本? (如ALTER脚本)
    • 一切?
    • 一切,和:
      • 不要忘记静态数据(查找列表等),所以你不需要在环境之间复制任何数据
      • 只保留当前版本的数据库脚本(当然是受版本控制的)和
      • 存储ALTER脚本:1 BIG脚本(或名为001_AlterXXX.sql的脚本目录,以自然排列顺序运行它们将从版本A升级到B)
  3. 哪些types的对象不应该受版本控制?
    • 序列?
    • 资助?
    • 用户帐户?
    • 请参阅2.如果您的用户/angular色(或技术用户名)在环境之间不同,您仍然可以使用模板对其进行编写脚本(请参阅1.)
  4. 数据库对象应该如何在您的SCM存储库中进行组织?
    • 你如何处理像转换脚本或ALTER脚本一次性的东西?
    • 见2。
    • 你如何处理来自数据库的退休对象?
    • 从数据库中删除,从源代码控制中继/提示中删除
    • 谁应该负责推动对象从开发到testing水平?
    • 开发/testing/发布时间表
    • 你如何协调来自多个开发人员的变化?
    • 尽量不要为每个开发者创build一个单独的数据库。 你使用源代码控制,对吧? 在这种情况下,开发人员更改数据库并检入脚本。 为了完全安全,在夜间构build期间从脚本重新创build数据库
    • 你如何处理多个系统使用的数据库对象的分支?
    • 艰难的一个:尽量避免不惜一切代价。
  5. 如果有的话,有什么例外可以合理地做到这一过程?
    • 安全问题?
    • 不要存储testing/产品的密码。 你可以允许它用于开发,特别是如果你有自动日/夜DB重build
    • 数据与去识别的关注?
    • 脚本不能完全自动化?
    • 文档和存储与发布信息/ ALTER脚本
  6. 你如何使这个过程具有弹性和可执行性?
    • 要开发人员错误?
    • 从头开始testing每日构build,并将结果与​​增量升级(使用ALTER从版本A到B)进行比较。 比较产生的模式和静态数据
    • 意想不到的环境问题?
    • 使用版本控制和备份
    • 将PROD数据库模式与您的想法进行比较,特别是在部署之前。 SuperDuperCool DBA可能已经修复了从未在您的票务系统中的错误:)
    • 为了灾难恢复?
  7. 你如何说服决策者,DB-SCM的好处真正certificate了成本?
    • 传闻?
    • 行业研究?
    • 行业最佳实践build议?
    • 呼吁公认的权威机构?
    • 成本效益分析?
    • 如果开发人员和数据库pipe理员同意,你不需要说服任何人,我想(除非你需要钱购买像MSSQL的dbGhost软件)
  8. 谁应该在这个模型中“拥有”数据库对象?
    • 开发商?
    • 数据库pipe理员?
    • 数据分析师?
    • 超过一个?
    • 通常,DBA批准模型(在签入之前或之后作为代码审查的一部分)。 他们肯定拥有与性能相关的对象 但总的来说,球队拥有它(当然是雇主):)]

我尽可能将SQL视为源代码

如果我可以将它写入标准的兼容SQL中,那么它通常会放在我的源代码pipe理中的一个文件中。 该文件将尽可能定义诸如SP,Table CREATE语句。

我还包括在源代码控制中进行testing的虚拟数据:

  1. 凸出/ SQL / setup_db.sql
  2. 凸出/ SQL / dummy_data.sql
  3. 凸出/ SQL / mssql_specific.sql
  4. 凸出/ SQL / mysql_specific.sql

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

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

我们通过TeamCity持续集成。 在每次签入源代码控制时,数据库和所有testing数据都是从头开始重新构build,然后是代码,然后是unit testing运行代码。 如果您正在使用CodeSmith这样的代码生成工具,那么也可以将其放置到您的构build过程中,以在每个构build中生成新的数据访问层,确保所有图层“匹配”,并且不会因为不匹配的SP参数或丢失的列。

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

根据查找表,我们的大多数查找值也存储在脚本中,并运行以确保configuration数据是我们所期望的,例如“reason_codes”或“country_codes”。 通过这种方式,我们可以在dev中对查找数据进行更改,对其进行testing,然后通过QA和生产“推广”,而不是使用工具来修改生产中的查找值,这对正常运行时间可能是危险的。

我们还创build了一组“回滚”脚本,用于撤销数据库更改,以防生成到生产环境变得棘手。 您可以通过运行回滚脚本来testing回滚脚本,然后在部署脚本运行后重新运行构build版本的unit testing。

通过询问“传情问题”,你似乎对某个讨论比对某人最终答案的意见更感兴趣。 活跃的(> 2500个成员)邮件列表agileDatabases已经解决了这些问题中的许多问题,以我的经验来说,这是一个复杂的公民论坛。

我基本上同意范的每一个答案。 为了更深入的了解,我的数据库pipe理基准是K. Scott Allen系列 (必须阅读,恕我直言,以及Jeff的看法 )。

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

恕我直言, dynamic数据加载应该需要另一个步骤,取决于你的环境。 开发人员会想要加载他们的数据库,testing,垃圾或根本没有数据,而在另一端生产经理将要加载生产数据。 我会考虑将testing数据存储在源代码控制中(例如,为了简化unit testing)。

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

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

与此有关的问题是:

  • 根据数据库供应商,很难自动比较数据库模式。 这可能会导致升级脚本不完整。
  • 生产环境的每一个变化(通常由DBA进行性能调整)都应该find源代码pipe理的方式。 为了确保这一点,通常可以通过触发器将每个修改logging到数据库。 此日志在每次升级后都会重置。
  • 但更理想的情况是,尽可能DBA发起的更改应该是发布/升级过程的一部分。

至于你想在源代码控制下有什么样的数据库对象? 那么,我会尽可能地说,但不是更多;-)如果你想创build密码的用户,让他们一个默认的密码(login/login,实用的unit testing目的),并使密码更改为手动操作。 Oracle在模式也是用户的情况下会发生这种情况。

Liquibase +1: LiquiBase是一个开放源代码(LGPL),独立于数据库,用于跟踪,pipe理和应用数据库更改。 它build立在一个简单的前提上:所有数据库更改(结构和数据)都以基于XML的描述方式进行存储并签入源代码pipe理。 好处是,DML的变化是存储在语义上,而不仅仅是差异,所以你可以跟踪变化的目的。

它可以与GIT版本控制相结合,以获得更好的交互。 我要configuration我们的dev-prod环境来尝试一下。

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

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

除此之外 ,您还可以使用DBUnit进行数据库testing – 这个工具允许数据生成脚本用于testing您的生产环境,并随后进行清理。

恕我直言:

  1. 将DML存储在文件中,以便可以对其进行版本化。
  2. 从源代码控制中自动化模式构build过程。
  3. 出于testing目的,开发人员可以使用通过构build系统+负载testing从源代码pipe理构build的本地数据库使用脚本或DBUnit脚本(来自“源代码pipe理”)的数据。
  4. LiquiBase允许您提供脚本的“运行顺序”以尊重依赖性。
  5. 应该有DBA团队在生产使用前检查主变更与所有更改。 我的意思是他们在进入MASTER中继之前,检查其他DBA的干线/分支。 这样,主人总是一致的,生产就绪。

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

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

对于部署来说这是不可能的,因为数据库太大了,这就是把它们放在数据库中的主要原因。

我坚信数据库应该是源代码控制的一部分,也是构build过程的一个重要组成部分。 如果是在源代码控制中,那么在编写SQL语言中的存储过程时,我将使用相同的编码安全防护,就像在C#中编写类时一样。 我通过在我的源代码树下包含一个DB脚本目录来实现这一点。 该脚本目录不一定有一个文件用于数据库中的一个对象。 这将是一个痛苦的屁股! 我开发我的分贝只是我会在我的代码项目。 然后,当我准备好签入时,我会在我的数据库的最新版本和我正在使用的当前数据库之间做一个差异。 我使用SQL Compare进行比较,并生成所有更改的脚本。 这个脚本然后被保存到我的db_update目录中,并使用一个特定的命名约定1234_TasksCompletedInThisIteration,其中数字是已经存在的脚本集合中的下一个数字,名称描述了在这个签入中正在做什么。我这样做是因为我的构build过程的一部分,我开始一个新的数据库,然后编程使用该目录中的脚本build立。 我写了一个自定义的NAnt任务,遍历每个脚本在裸数据库上执行其内容。 显然,如果我需要一些数据进入数据库,那么我也有数据插入脚本。 这也有很多好处。 其一,我所有的东西都是版本化的。 二,每一次构build都是一个新鲜的构build,这意味着不会有任何偷偷摸摸的东西进入我的开发过程(例如导致系统中的怪异的脏数据)。 三,当一个新人join到开发团队,他们只需要得到最新的,他们的本地开发是为他们在飞行中build立。 四,我可以在我的数据库上运行testing用例(我没有称之为“unit testing”!),因为数据库的状态在每次构build时都被重置(这意味着我可以testing我的存储库,而不用担心将testing数据添加到D b)。

这不适合每个人。

这不是每个项目。 我通常在绿地项目上工作,这让我方便!

这里有一个解决scheme,对于我在现实世界的问题上非常有效。

从头开始构build数据库可以概括为pipe理SQL脚本。

DBdeploy是一个工具,它将检查数据库的当前状态 – 例如,以前运行过哪些脚本,可以运行哪些脚本,以及需要运行哪些脚本。

然后将所有需要的脚本整理在一起并运行。 然后logging哪些脚本已经运行。

这不是最漂亮的工具,也不是最复杂的工具,但通过仔细的pipe理,它可以很好地工作。 它是开源的,易于扩展。 一旦脚本的运行得到很好的处理,就可以轻松地添加一些额外的组件,比如用来检出最新脚本的shell脚本,并且针对特定的实例运行dbdeploy。

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

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

你可能会发现Liquibase处理了很多你正在寻找的东西。

每个开发者都应该有自己的本地数据库,并使用源代码控制来发布给团队。 我的解决scheme是在这里: http : //dbsourcetools.codeplex.com/玩得开心, – 弥敦道