提问



我正在做一个Web应用程序,我需要为一些重大更改创建一个分支,事实是,这些更改需要更改数据库模式,所以我想把整个数据库放在git下面。


我怎么做?是否有一个特定的文件夹,我可以保存在git存储库下?我怎么知道哪一个?我怎么能确定我正在放置正确的文件夹?


我需要确定,因为这些变化不向后兼容;我不能搞砸了。


我的数据库是PostgreSQL


编辑:



有人建议进行备份并将备份文件置于版本控制下而不是数据库中。说实话,我发现真的难以接受。


必须有更好的方法。


更新



好吧,所以没有更好的方法,但我仍然不太相信,所以我会稍微改变一下这个问题:


我想把整个数据库置于版本控制之下,我可以使用什么数据库引擎,以便我可以将实际数据库置于版本控制下而不是转储?


sqlite会对git友好吗?


由于这只是开发环境,我可以选择我想要的任何数据库。


EDIT2:



我真正想要的不是跟踪我的开发历史,而是能够从我的新的激进变化分支切换到当前稳定分支,并能够用当前的方法解决一些错误/问题等稳定的分支。这样,当我切换分支时,数据库自动神奇地变得与我当前所在的分支兼容。
我真的不太关心实际的数据。

最佳参考


取一个数据库转储和版本控制。这样它就是一个平面文本文件。


我个人建议您同时保留数据转储和架构转储。通过这种方式使用diff,可以很容易地看到模式中从修订版本到修订版本的变化。


如果要进行大的更改,则应该有一个辅助数据库,您可以将新模式更改为旧模式,而不是触及旧模式,因为您说您正在创建分支。

其它参考1


查看重构数据库(http://databaserefactoring.com/),了解一系列良好的技术,以便与代码更改一起维护数据库。[6]


可以说你提出了错误的问题。你应该将你的更改分解为小的可验证步骤,而不是将数据库放入git,这样你就可以轻松地迁移/回滚模式更改。


如果您希望具有完全可恢复性,则应考虑存档postgres WAL日志并使用PITR(时间点恢复)将事务回放/转发到特定的已知良好状态。

其它参考2


我开始想到一个非常简单的解决方案,不知道为什么我之前没有想到它!



  • 复制数据库(架构和数据)。

  • 在新主要更改的分支中,只需更改项目配置即可使用新的重复数据库。



这样我就可以切换分支而不必担心数据库模式的变化。


编辑:



副本,我的意思是创建另一个具有不同名称的数据库(如my_db_2);不做转储或类似的事情。

其它参考3


不要手动转储数据库并将其保存到git中,而是使用Offscale DataGrove。[7]


DataGrove基本上是一个数据库版本控件 - 它跟踪对整个数据库(模式和数据)的更改,并允许您将版本标记到它的存储库中。您可以将它与git一起使用,并在每次登记时使用它标记版本代码,并在您提取代码时加载正确的DB状态。


特别是关于编辑2 - 使用DataGrove,您可以简单地拥有DB的两个分支,每个分支对应一个代码分支。当您加载代码的某个分支时,DataGrove将自动重新创建整个数据库状态,其中包含该版本/分支的所有数据。这意味着您可以使用一个简单的命令在开发分支之间切换。

其它参考4


使用像LiquiBase之类的东西,这可以让你保持对Liquibase文件的修订控制。您可以仅为生产标记更改,并让您的数据库保持最新的生产或开发,(或您想要的任何方案)。[8]

其它参考5


目前正在开发一种名为Klonio的工具,其测试版可供使用。它到目前为止支持MongoDB和MySQL。[9]


当然,它具有git集成,您可以单独为您的模式或甚至包含的数据创建快照。

其它参考6


在Doctrine下有一个名为Migrations的伟大项目,仅为此目的而构建。


它仍处于alpha状态并为php构建。


http://docs.doctrine-project.org/projects/doctrine-migrations/en/latest/index.html[10]

其它参考7


看看RedGate SQL Source Control。


http://www.red-gate.com/products/sql-development/sql-source-control/[11]


此工具是一个SQL Server Management Studio管理单元,允许您使用Git将数据库置于Source Control下。


它的价格有点贵,每位用户495美元,但有28天的免费试用版。


注意
我不以任何方式与RedGate有任何关系。

其它参考8


我已经遇到过这个问题,因为我遇到了类似的问题,其中某些东西接近基于数据库的目录结构,存储文件,我需要git来管理它。它通过云分布,使用复制,因此它的访问点将通过MySQL。


上述答案的要点似乎同样暗示了问题的另一种解决方案,即使用Git来管理数据库中的某些内容,这种方法有点遗漏,所以我将尝试回答这个问题。


Git是一个系统,它实质上存储了增量数据库(差异),可以按顺序重新组合,以重现上下文。 git的正常用法假设上下文是一个文件系统,那些增量是该文件系统中的差异,但实际上所有的git都是增量的分层数据库(层次结构,因为在大多数情况下每个增量都是一个提交,至少1个父,安排在树上)。


只要你能生成一个delta,理论上git就可以存储它。问题通常是git期望它生成delta的上下文是一个文件系统,类似地,当你签出git层次结构中的一个点时,它期望生成一个文件系统。


如果你想管理变更,在数据库中,你有两个不连续的问题,我会分别解决它们(如果我是你)。第一个是模式,第二个是数据(尽管在你的问题中,你说的数据不是你所关心的事情)。我过去遇到的一个问题是Dev和Prod数据库,其中Dev可以对模式进行渐进式更改,这些更改必须记录在CVS中,并传播到live,以及添加到几个静态之一表。我们通过拥有一个名为Cruise的第三个数据库来实现这一点,该数据库仅包含静态数据。在任何时候都可以比较Dev和Cruise的模式,并且我们有一个脚本来获取这两个文件的差异并生成包含ALTER语句的SQL文件,以应用它。类似地,任何新数据都可以被提炼为包含INSERT命令的SQL文件。只要添加了字段和表,并且永远不会删除它们,该过程就可以自动生成SQL语句以应用增量。


git生成增量的机制是diff,它将一个或多个增量与文件组合的机制称为merge。如果你能想出一种从不同的上下文中进行差异和合并的方法,git应该可以工作,但正如我们已经讨论过的那样,你可能更喜欢一种能够为你做到这一点的工具。我首先想到的是解决这个问题的https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration#External-Merge-and-Diff-Tools,详细说明了如何替换git的内部差异和合并工具。我会更新这个答案,因为我想出了一个更好的问题解决方案,但在我的情况下,我希望只需管理数据更改,因为基于数据库的文件存储可能改变,所以我的解决方案可能不是你需要的。 [12]

其它参考9


你无法在没有原子性的情况下做到这一点,并且无需使用pg_dump或快照文件系统就无法获得原子性。


我的postgres实例是在zfs上,我偶尔会快照一下。它几乎是即时和一致的。

其它参考10


在精神上,你想要的可能就像Post Facto,它将数据库的版本存储在数据库中。查看此演示文稿。[13] [14]


该项目显然从来没有真正去过任何地方,所以它可能不会立即帮助你,但这是一个有趣的概念。我担心这样做是非常困难的,因为即使是版本1也必须正确地获取所有细节才能让人们相信他们的工作。

其它参考11


我已经发布了一个sqlite工具,可以满足您的要求。它使用自定义diff驱动程序,利用sqlite项目工具sqldiff,UUID作为主键,并且不使用sqlite rowid。它仍处于alpha状态,因此感谢您的反馈。


Postgres和mysql比较棘手,因为二进制数据保存在多个文件中,如果能够对其进行快照,则甚至可能无效。


https://github.com/cannadayr/git-sqlite[15]

其它参考12


我想做类似的事情,将我的数据库更改添加到我的版本控制系统。


我将按照Vladimir Khorikov数据库版本最佳实践这篇文章中的想法进行操作。总之,我会[16]



  • 将其架构和参考数据存储在源控制系统中。

  • 对于每次修改,我们将使用更改
  • 创建单独的SQL脚本


如果它有帮助!

其它参考13


我认为X-Istence正在走上正轨,但您可以对此策略进行一些改进。首先,使用:


$pg_dump --schema ... 


转储表,序列等,并将此文件置于版本控制之下。您将使用它来分隔分支之间的兼容性更改。


接下来,为包含配置 required 的表集执行数据转储,以便应用程序运行(应该可能跳过用户数据等),例如表单默认值和其他数据非用户可修改数据。您可以通过以下方式选择性地执


$pg_dump --table=.. <or> --exclude-table=..


这是一个好主意,因为当数据库在执行完整数据转储时达到100Mb +时,repo会变得非常笨重。更好的方法是备份测试应用程序所需的最小数据集。如果您的默认数据非常大,但这可能仍会导致问题。


如果您绝对需要在repo中放置完整备份,请考虑在源树之外的分支中执行此操作。尽管如此,外部备份系统可能最适合使用匹配的svn rev。


此外,我建议使用文本格式转储而不是二进制文件用于修订目的(至少对于模式),因为这些转换更容易区分。您可以在登记前始终压缩这些以节省空间。


最后,看看postgres备份文档,如果你还没有。你评论备份数据库而不是转储的方式让我想知道你是否在考虑基于文件系统的备份(参见章节) 23.2注意事项)。[17] [18]

其它参考14


这个问题已经得到了很好的回答,但我想用一个小小的建议补充X-Istence和Dana the Sane的答案。


如果您需要具有某种程度的粒度的版本控制,比如每天,您可以将表和模式的文本转储与rdiff-backup之类的工具耦合,后者执行增量备份。优点是,您只需存储前一天的差异,而不是存储每日备份的快照。[19]


有了这个,你既有修改控制的优势,又不会浪费太多空间。


无论如何,直接在非常频繁变化的大平面文件上使用git并不是一个好的解决方案。如果您的数据库太大,git将开始在管理文件时遇到一些问题。

其它参考15


我建议neXtep用于控制数据库的版本,它有一套很好的文档和论坛,解释了如何安装和遇到的错误。我已经测试了它的postgreSQL 9.1和9.3,我能够让它适用于9.1但是9.3它似乎不起作用。[20]

其它参考16


我在个人项目中所做的是,我将整个数据库存储到dropbox,然后指向MAMP,WAMP工作流程,直接从那里使用它。这样,数据库始终是最新的,我需要做一些开发。但这只是为了开发!Live网站正在使用自己的服务器!:)

其它参考17


在git版本控制下存储每个级别的数据库更改就像在每次提交时推送整个数据库,并在每次提取时恢复整个数据库。
如果您的数据库很容易发生重大变化,并且您无法放松它们,则只需更新 pre_commit post_merge 挂钩即可。
我对我的一个项目做了同样的事情,你可以在这里找到方向。[21]

其它参考18


那是我怎么做的:


由于您可以自由选择数据库类型,因此使用基于文件的数据库,例如:火鸟。


创建一个模板DB,其模式适合您的实际分支并将其存储在您的存储库中。


以编程方式执行应用程序时,请创建模板数据库的副本,将其存储在其他位置,然后使用该副本。


这样,您可以将数据库架构置于版本控制之下而无需数据。如果您更改架构,则只需更改模板DB即可

其它参考19


我们曾经在标准LAMP配置上运行社交网站。我们有一个Live服务器,测试服务器和开发服务器,以及本地开发人员机器。所有都是使用GIT管理的。


在每台机器上,我们都有PHP文件,还有MySQL服务,以及一个用户可以上传的图像文件夹。 Live服务器增长了大约100K(!)的经常性用户,转储大约2GB(!),Image文件夹大约50GB(!)。到我离开的时候,我们的服务器达到了它的CPU,Ram的限制,最重要的是,并发的网络连接限制(我们甚至编译了我们自己的网卡驱动程序版本以最大化服务器lol)。我们不能(你也不应该假设你的网站)在GIT中放置了2GB的数据和50GB的图像。


要在GIT下轻松管理所有这些,我们会通过将这些文件夹路径插入.gitignore来忽略二进制文件夹(包含图像的文件夹)。我们在Apache documentroot路径之外还有一个名为SQL的文件夹。在那个SQL文件夹中,我们将来自开发人员的SQL文件放在增量编号中(001.florianm.sql,001.johns.sql,002.florianm.sql等)。这些SQL文件也由GIT管理。第一个sql文件确实包含大量的数据库模式。我们不在GIT中添加用户数据(例如,用户表或注释表的记录),但是在sql文件中(因此通过GIT)维护了诸如配置或拓扑或其他站点特定数据之类的数据。主要是它的开发人员(他们最了解代码)确定GIT在SQL模式和数据方面的内容和内容。


当它发布时,管理员登录到开发服务器,将实时分支与开发机器上的所有开发人员和所需分支合并到更新分支,并将其推送到测试服务器。在测试服务器上,他检查Live服务器的更新过程是否仍然有效,并且快速连续,将Apache中的所有流量指向占位符站点,创建数据库转储,将工作目录从实时指向更新 ,将所有新的sql文件执行到mysql中,并将流量重新发送回正确的站点。当所有利益相关者在审核测试服务器后达成一致意见时,管理员在测试服务器和Live服务器上做了同样的事情。之后,他将生产服务器上的实时分支合并到所有服务器的主分支,并重新绑定所有实时分支。开发人员负责自己修改分支机构,但他们通常知道他们在做什么。


如果测试服务器出现问题,例如。合并有太多的冲突,然后代码被还原(指向工作分支回活)并且从未执行过sql文件。在执行sql文件的那一刻,这被认为是当时不可逆的动作。如果SQL文件无法正常工作,则使用转储恢复数据库(开发人员告知,提供经过严格测试的SQL文件)。


今天,我们维护一个sql-up和sql-down文件夹,具有等效的文件名,开发人员必须测试升级的sql文件,可以同等降级。这最终可以使用bash脚本执行,但如果人眼继续监视升级过程,这是一个好主意。


它不是很好,但它可以管理。希望这能让我们深入了解一个真实,实用,相对高可用性的网站。它有点过时,但仍然遵循。

其它参考20


使用像iBatis Migrations(手册,简短的教程视频)这样的工具,它允许您在项目的整个生命周期中对数据库进行版本控制,而不是数据库本身。[22]]] [23]


这允许您有选择地将单个更改应用于不同的环境,保留更改日志在哪些环境中更改,创建脚本以应用更改A到N,回滚更改等。

其它参考21



  我想把整个数据库置于版本控制之下,什么
  我可以使用数据库引擎,以便我可以将实际的数据库放在下面
  版本控制而不是其转储?



这不依赖于数据库引擎。通过Microsoft SQL Server,有许多版本控制程序。我不认为问题可以通过git解决,你必须使用pgsql特定的架构版本控制系统。我不知道这样的事情是否存在......

其它参考22


这是我在我的项目中尝试做的事情:



  • 单独的数据和架构以及默认数据。



数据库配置存储在不受版本控制的配置文件中(.gitignore)


数据库默认值(用于设置新项目)是版本控制下的简单SQL文件。


对于数据库模式,在版本控制下创建数据库模式转储。


最常见的方法是拥有包含SQL语句的更新脚本(ALTER Table ..或UPDATE)。您还需要在数据库中有一个位置,用于保存当前版本的模式)


看看其他大型开源数据库项目(piwik,或者你最喜欢的cms系统),它们都使用updatescripts(1.sql,2.sql,3.sh,4.php.5.sql)


但这是一项非常耗时的工作,您必须创建并测试更新脚本,并且需要运行一个通用的更新脚本来比较版本并运行所有必需的更新脚本。


所以理论上(这就是我要找的东西)你可以
每次更改后转储数据库模式(手动,结合,git挂钩(可能在提交之前))
(并且仅在一些非常特殊的情况下创建更新脚本)


之后在您的通用更新脚本中(针对特殊情况运行正常的更新脚本),然后比较模式(转储和当前数据库),然后自动生成非必需的ALTER语句。有一些工具可以做到这一点,但还没有找到一个好的工具。

其它参考23


面对类似的需求,这就是我对数据库版本控制系统的研究:



  1. Sqitch - 基于perl的开源;适用于所有主要数据库,包括PostgreSQL [24] https://github.com/sqitchers/sqitch

  2. Mahout - 仅适用于PostgreSQL;开源数据库模式版本控制。
    [25] https://github.com/cbbrowne/mahout

  3. Liquibase - 另一个开源db版本控制sw。免费版的Datical。[26] http://www.liquibase.org/index.html

  4. 数据 - Liquibase的商业版本 - [27] https://www.datical.com/

  5. BoxFuse的Flyway - 商业sw。 [28] https://flywaydb.org/

  6. 另一个开源项目[29] https://gitlab.com/depesz/Versioning
    作者在此提供指南:[30] https://www.depesz.com/2010/08/22/versioning/

  7. 红门更改自动化 - 仅适用于SQL Server。
    [31] https://www.red-gate.com/products/sql-development/sql-change-automation/