复杂分支系统中的数据库迁移

时间:2011-06-20 09:27:09

标签: git git-branch database-migration database-versioning

在我们当前的开发工作流程中,我们引入了数据库迁移(使用Ruckusing)以使我们的开发人员的db模式保持同步。它工作得很好,使用起来非常简单,但现在我们已经切换到git作为VCS,我们正面临数据库版本控制系统中的下一个问题。

当签出已经开发一段时间的分支时,可能会发生数据库模式与我来自的分支中的模式分歧很多。这在某些情况下会导致数据库冲突。从逻辑上讲,我们似乎需要根据我们以前的分支运行迁移,但这可能会非常快速地复杂化并且肯定会遇到一些人的问题。据我所知,没有分支感知的数据库迁移系统??

当切换到功能分支时增加了复杂性我们可能需要运行一些迁移而其他功能下降...技术上这似乎不可能使用我们当前的dbmigration脚本,是否有任何合理的替代方案?在非常活跃和分支的开发系统中是否有任何首选的数据库迁移方法?

5 个答案:

答案 0 :(得分:28)

我不同意增量迁移被腐烂。在我看来,拥有一套自己开发的脚本比使用真正的工具来实现这样的工作会更容易跟踪这些更改。我以前必须自己处理类似的情况,所以希望我可以分享一些见解。

根据我的经验,RDBMS模式和分支不能很好地混合。根据您的分支,模式应该至少有些相似,在这种情况下,迁移不应该有太大差异。或者我可能只是误解了问题的全部范围。如果你是试图将客户特定的代码保留在分支上,那么也许你应该考虑一种模块化的方法。我们做了类似的事情,有规则说客户特定的架构发生了变化,代码只能依赖于公共代码库,而不是相反。我们还根据模块和日期设置模块更改集之间的优先级,因此我们对大多数部分了解更改的应用顺序。当然,YMMV,但在不知道您当前的设置的情况下很难给出细节。

在我的旧公司,我们成功使用了一个名为Liquibase的工具,听起来与您正在使用的工具类似。基本上它是一个用于获取数据库模式的工具,以及从一个已知状态到另一个已知状态的所有数据。同一个变更集只应用一次,因为liquibase维护一个带校验和的更改日志。更改日志以特定的XML格式编写。如果您需要其他选择,我强烈建议您尝试一下。

无论如何,我们处理客户代码和分支的方式是为给定的分支设置特定的DB /模式。这样,您可以从分支点获取架构和数据,并仅将diff迁移到当前情况。我们没有撤消变化,即使理论上的liquibase可以支持这一点,因为我们认为它太麻烦且容易出错。鉴于liquibase保持自己的状态,迁移总是像在给定分支上获取当前状态一样简单,并应用全部。仅应用了新的更改集,使架构处于良好状态。

我们使用mercurial,它是像git一样分发的,因此设置非常相似。我们还在开发笔记本电脑上设置了开发人员特定的本地数据库,以及针对不同客户和阶段(开发,集成,生产)的许多环境,因此该模型进行了真正的测试,并且效果出奇的好。我们在变更集中遇到了一些冲突,但我们大多能够在问题出现后很快解决这些冲突。本地开发环境确实是最困难的部分,因为在开发过程中可能会引入一些模式更改,这些更改并不总是与以后的更改集兼容,而是更改的结构化特性,并且具有已知状态可以恢复为极少数真正的问题。

这种方法有一些注意事项:

  1. 必须在更改集中实现对架构的所有和任何更改。困惑的最大原因总是有人只是摆弄了一下。
  2. 第一点也适用,即使您使用的是修改架构的工具,例如像Hibernate这样的ORM工具。你需要与这个工具非常亲密,以了解它所做的和需要的变化。
  3. 所有用户都必须购买,并接受教育以遵守规则。检查1。
  4. 当迁移 lot 变更集开始花费太多时间时,就会出现这种情况。此时你需要创建一个新的基线,这可能有点棘手,特别是有很多分支。提前做好计划也是好事,至少知道所有现有的DB分支。
  5. 您需要提前计划分支,以了解他们是否会在某个时候迁移回主人。朴素合并可能不适用于架构更改。
  6. 对于寿命很长的分支和分离的数据集,此模型可能不够强大
  7. 然而,关键是,您对数据库的结构和控制越多,迁移就越容易。因此,像Liquibase这样的工具可以成为帮助您跟踪这些变化的真正有价值的资产。这比更简单的模型更适用于更复杂的模型,所以请至少不要考虑转储现有的所有工具。并花些时间探索其他替代工具。

    一些结构和控制总比没有好,甚至更糟,认为你掌握了大量的手动脚本。

答案 1 :(得分:15)

我认为增量迁移的整个想法非常糟糕,真的。在像你这样的复杂环境中,它确实不起作用。你可以让它适用于简单的分支模式,但对于任何复杂的事情,它都将是一场噩梦。

我正在使用的系统采用不同的方法:我们无法进行增量迁移,只能从基线重建数据库。在初始开发期间,该基线是一个空数据库,在维护期间,它是实时数据库的副本(从转储中恢复)。我们只有一堆SQL和XML脚本,我们应用于基线以获得当前系统(本质上是迁移,但不是设计为以递增方式运行)。更新或切换分支非常简单:核对数据库,加载转储以建立基线,运行脚本。

这个过程不像运行一些迁移那么快,但它足够快。这需要足够长的时间才能拿到一杯咖啡,但不能长到午餐。

巨大的优势在于,通过对数据库进行核对意味着该过程完全独立于历史,因此不需要知道或关心跨越分支,回溯过去或其他任何事情。

当您进行实时发布时,您显然做的事情略有不同:您没有核数据库或加载转储,因为系统已经处于基线状态(基线被定义为实时系统的状态! )。你只需运行脚本。然后,进行新的转储,作为开发的新基准。

答案 2 :(得分:2)

我处于类似的情况,我在一个实时网站和几个开发分支机构工作,我需要在其中更改数据库架构。

我通过写一个post-checkout和一个合并后的钩子来解决它,它可以很好地与git一起使用。我将所有迁移以SQL文件的形式存储在一个单独的目录中,并将它们与更改的PHP代码一起提交。每次我执行

git checkout

git merge

git会自动调用适当的上下迁移。请参阅Github上的实施。

作为特殊要求(对于那些不想跟随github链接的人),请提供更多解释:

请考虑以下情形。你有两个分支:

  • master - 包含当前在线的网站
  • 功能 - 包含未完成的新功能

要使新功能正常工作,需要更改数据库架构。 工作流程如下:

  1. 当您在功能分支中更改需要更改的代码时 在数据库模式中,您还要在迁移目录中提交两个新的SQL文件, 说:

    • 20151120130200-extra-field-up.sql(包含要迁移的所有SQL查询 向上)
    • 20151120130200-extra-field-down.sql(包含要迁移的所有SQL查询 向下)
  2. 当您现在执行结帐以掌握时,后收到git挂钩将:
    1. <new HEAD>..<old HEAD>
    2. 的提交中查找所有* -down.sql脚本
    3. 使用本地数据库执行这些脚本
    4. <old HEAD>..<new HEAD>
    5. 的提交中查找所有* -up.sql脚本
    6. 使用本地数据库执行这些脚本
  3. 将功能分支合并到主服务器时,合并后的挂钩将:
    1. master..feature
    2. 的提交中查找所有* -up.sql脚本
    3. 使用本地数据库执行这些脚本
  4. 安装

    只需将结帐后和/或合并后文件复制到.git / hooks即可 你自己的git存储库的目录。您可以编辑它们的配置部分 文件。请参阅文件本身以获得解释。

    用法

    迁移SQL文件的命名至关重要。他们应该结束 up.sqldown.sql。名称的其余部分完全取决于您。 但是,如果您有一个具有多个向上迁移和/或多个迁移的提交 向下迁移它们的执行顺序取决于 字典顺序。将在不同提交内的迁移文件 总是以与提交相同的(反向)顺序调用。

    并不要求您同时具有向上迁移和向下升级, 也不要求上下迁移被命名为相似。

答案 3 :(得分:1)

我正在考虑在当前项目中进行测试的方法是创建分支“迁移”,并且所有(并且仅)的迁移都将提交到此分支。在创建迁移之前,开发人员必须从此分支合并到其当前分支,以便始终在最新迁移之上创建迁移。所有项目都从此分支合并,因此每个分支都具有线性迁移历史记录的概念。这使每个分支都能够在数据库版本之间来回移动。当切换到依赖于不同版本数据库的分支时,开发人员应用适合的迁移。

烦恼(除了向特殊分支提交迁移的额外工作和勤奋之外)是记住哪个迁移对应于特定分支。我想这样做的一种方法是将迁移直接提交到迁移分支,而不是将迁移(仅迁移)提交到当前分支,然后将提交提交到迁移分支。然后,您可以查看当前分支机构最后一次挑选到迁移分支,并知道该差异包含必要的迁移。我认为这是可能的。此外,开发人员可能只是为了查看需要进行哪些更改而创建迁移,然后尝试推断哪些迁移适合使用。

对于模糊的建议,我们深表歉意;如果我们最终尝试这种方法,我会用更具体的建议来编辑这个建议。

答案 4 :(得分:0)

这是我最近一直在努力的事情。对我来说问题不是数据库模式本身有分歧,而是git不能将它们合并在一起。触及数据库模式的功能分支总是很可怕。

我一直在考虑的解决方案是,不是进行线性迁移,而是进行依赖于其他迁移的迁移。您可以获得一个很好的迁移依赖图,它很容易线性化(拓扑排序)。只需跟踪数据库中的命名迁移,并按正确的顺序执行尚未更新的更新。

例如,addCustomerSalt取决于initialSchemaseparateAddress取决于person

这个问题没有解决的一个问题是,如果分支A依赖于在分支B中创建的更新Z,但是在这种情况下,你应该重新命名为共同的祖先吗?