如何将数据库置于git(版本控制)下?


274

我正在做一个Web应用程序,我需要对一些主要更改进行分支,事实是,这些更改需要更改数据库架构,因此我也希望将整个数据库也置于git下。

我怎么做?我可以在git资料库中保留一个特定的文件夹吗?我怎么知道哪一个?如何确定要放入正确的文件夹?

我需要确定,因为这些更改不是向后兼容的。我无力承担。

我的数据库是PostgreSQL

编辑:

有人建议进行备份,并将备份文件而不是数据库置于版本控制之下。老实说,我很难吞咽。

一定有更好的方法。

更新:

好的,因此没有更好的方法,但是我仍然不太确定,因此我将稍微改变一下问题:

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

sqlite是git友好的吗?

由于这只是开发环境,因此我可以选择所需的任何数据库。

编辑2:

我真正想要的不是跟踪我的开发历史,而是能够从我的“新的根本性变化”分支切换到“当前稳定的分支”,并能够例如使用当前版本修复一些错误/问题等。稳定分支。这样,当我切换分支时,数据库会自动与我当前所在的分支兼容。我不太在乎实际数据。


5
老实说,如果我要引入模式更改并且必须同时处理多个开发分支,那么我只是复制数据库。...希望开发数据库应该足够小才能做到这一点。我认为任何试图变得聪明并进行数据库更改的系统,只是因为我怀疑地更改了源分支。而且我还想确保,如果我只是克隆我的工作区并在一个位置有一个分支,而在新位置有另一个分支,那么一切都会继续进行。
araqnid,2009年


如果您认为脚本(及其组件)将数据库初始化为版本控制下的工件,那么“备份”似乎并不是一件坏事。如果在基本分支中更改数据库模式,则需要更新使用数据初始化数据库的脚本。
Fuhrmanator 2014年

1
签出我的答案,
Kevin

Answers:


140

进行数据库转储,然后进行版本控制。这样,它是一个纯文本文件。

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

如果要进行大的更改,则应该有一个辅助数据库,您可以在其中更改新的架构,而不要触摸旧的数据库,因为正如您所说的,在进行分支。


132
什么?有一种更好的方法。
哈森

18
PostGreSQL数据库文件是二进制文件,可以将它们放在git存储库中,您将无法对它们进行任何差异,任何更改很可能会更改整个数据库,因此,您现在必须发送完整的数据库。通过电线将数据库存储到git repo并将其存储。这效率低下,速度慢,并且很难使用。另外,我不确定存储在磁盘上而没有VACUUM并关闭PostgreSQL进行复制的数据库文件是否“稳定”,因为所有数据始终都是正确的,因此可能会使您的数据损坏。
X-Istence,2009年

6
嗯,我明白了!好吧,有没有对git更友好的数据库系统?
哈森

16
这种类型的解决方案是非常标准和架构真正的源代码。
Dana the Sane

12
是2017年,对此问题有任何更新吗?实际上没有开箱即用的数据库版本控制吗?真的吗?
Stavm '17

48

查阅Refactoring Databases(http://databaserefactoring.com/),以获得一整套很好的技术来与代码更改保持数据库一致。

可以说您在问错问题。而不是将数据库放入git,您应该将更改分解为可验证的小步骤,以便您可以轻松迁移/回滚架构更改。

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


2
我没有在数据库重构站点上找到相关信息...似乎列出了DB代码的各种重构技术(就像Fowler对常规代码所做的那样)
Nickolay

26

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

  • 复制数据库(模式和数据)。
  • 在new-major-changes的分支中,只需将项目配置更改为使用新的重复数据库即可。

这样,我可以切换分支而不必担心数据库架构更改。

编辑:

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


3
这似乎是最简单,最有效的解决方案,但是如果有某种方法可以使它自动化,那就太好了……我很惊讶还没有任何东西……
JustMaier 2014年

git hook根据分支名称从模板创建数据库
dalore

这就是我的工作,我还为数据库变量的包含文件添加了IP检查行,这样,如果我不小心将“错误的”分支的文件上传到活动服务器,则不会中断。
liamvictor 2015年

几乎每个分支都拥有自己的数据库,是吗?🤔
奥利

19

使用类似LiquiBase的工具,这可以使您保持对Liquibase文件的版本控制。您只能将变更标记为仅用于生产,并且可以使您的数据库保持最新以进行生产或开发(或您想要的任何方案)。


3
Liguibase的最佳实践建议将架构创建脚本保留为一组顺序运行的顺序脚本。尽管这是一个很好的最佳实践,但我看不到没有中央存储库(un-GIT)如何工作。
Frank Schwieterman 2010年

1
好吧,如果您注意自己的id =和author =标记,那么跨git运行就可以了。理论上,每个用户都有自己的作者条目(GOOD),如果您对id =进行了合理的设置(例如YYYYMMDD_REV),那么您的选择就非常好。即使使用git,对于给定的项目,大多数人都有一个“中央仓库”。99%的人没有什么“中央”。同样,Liquibase文件只是计划文本XML格式的文件,带有一堆命令以针对给定的DB(或一组DB)执行。实际上,即使使用DVCS,在所有项目中有99%的项目在实践中会遇到0个问题。
zie

+1对于这个答案。这是我们在几个项目中使用的。ID仅在一个xml文件中唯一。如果您从正在实施的用例中命名ID,它们就足够独特。您必须小心不要修改已应用的变更集,否则会出现校验和错误。
2013年

7

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

  1. Sqitch-基于Perl的开源;适用于所有主要数据库,包括PostgreSQL https://github.com/sqitchers/sqitch
  2. Mahout-仅适用于PostgreSQL;开源数据库架构版本控制。 https://github.com/cbbrowne/mahout
  3. Liquibase-另一个开源数据库版本控制软件。免费版本的Datical。http://www.liquibase.org/index.html
  4. Datical - Liquibase的商业版本- https://www.datical.com/
  5. BoxFuse的Flyway-商业广告 https://flywaydb.org/
  6. 另一个开源项目https://gitlab.com/depesz/Versioning 作者在此处提供了指南:https//www.depesz.com/2010/08/22/versioning/
  7. 红门变更自动化-仅适用于SQL Server。 https://www.red-gate.com/products/sql-development/sql-change-automation/

过去也有所谓的东西ChronicDBChronicDB provides dynamic database upgrades with zero database downtime and inconsistencies. crunchbase.com/organization/chronicdb#section-overview一个叫Kristis Makris的人是其中一位创始人,也许以SCMBug着称:mkgnu.net/scmbug
ThorstenSchöning

6

有一个伟大的项目叫做教义,这就是为此目的而建立的。

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

http://docs.doctrine-project.org/projects/doctrine-migrations/zh-CN/latest/index.html


行动!您的链接已断开...也许您的意思是这样:github.com/doctrine/migrations
Francesco Casula

此处提供了集成了Symfony2中的学说迁移的捆绑软件的文档:symfony.com/doc/master/bundles/DoctrineMigrationsBundle/…–
Francesco Casula

1
感谢您的提示,Doctrine的人倾向于更改文档的位置,从而导致此处和Google上的许多断开的链接。修复了链接。
哈坎·德里亚

4

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

上述答案的要旨,似乎也为使用Git管理数据库中某些内容的问题提出了另一种解决方案,因此我将尝试回答该问题。

Git是一个系统,从本质上说,它存储增量(差异)数据库,可以对其进行重新组合以重现上下文。git的正常用法假定上下文是一个文件系统,并且这些增量是该文件系统中的diff,但实际上所有git都是一个增量的层次数据库(层次结构,因为在大多数情况下,每个增量都是至少具有1的提交父母,排列在树上)。

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

如果要管理变更,在数据库中,您有两个离散的问题,我将分别解决(如果您是我的话)。第一个是模式,第二个是数据(尽管在您的问题中,您声明数据不是您所关心的)。我过去遇到的一个问题是Dev和Prod数据库,其中Dev可以对模式进行增量更改,而这些更改必须在CVS中记录下来,并推广到其他版本的“静态”版本中表。为此,我们有了一个名为Cruise的第3个数据库,该数据库仅包含静态数据。在任何时候都可以比较Dev和Cruise的模式,我们有一个脚本来比较这两个文件的差异,并生成一个包含ALTER语句的SQL文件来应用它。同样,任何新数据,可以提取为包含INSERT命令的SQL文件。只要仅添加字段并且不删除字段和表,该过程就可以自动生成SQL语句以应用增量。

diff称为的git生成增量的机制,以及将一个或多个增量与一个文件组合的机制merge。如果您可以提出一种在不同上下文中进行差异化和合并的方法,则git应该可以工作,但是正如已经讨论过的那样,您可能更喜欢为您执行此操作的工具。我对解决这个问题的第一个想法是这个https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration#External-Merge-and-Diff-Tools,其中详细介绍了如何替换git的内部diff和合并工具。我将为这个问题提供一个更好的解决方案,以解决这个问题,但对于我而言,我希望只需要管理数据更改,因为基于数据库的文件存储可能会发生更改,因此我的解决方案可能不是您所需要的。



3

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

我将遵循弗拉基米尔·霍里科夫(Vladimir Khorikov)在“数据库版本控制最佳实践”一文中的想法。总而言之,我会

  • 将其架构和参考数据都存储在源控制系统中。
  • 对于每次修改,我们将使用更改创建一个单独的SQL脚本

万一有帮助!


3
  • 伊敏
  • Flur.ee
  • 关键数据库

我一直在寻找Postgres(或SQL数据库)的相同功能,但是我发现没有足够的工具适合(简单和直观)。这可能是由于数据存储方式的二进制性质。克朗尼奥听起来很理想,但看上去已经死了。Noms DB看起来很有趣(而且还很活跃)。还可以看一下Irmin(基于OCaml的Git属性)。

尽管这不能解决与Postgres一起使用的问题,但请查看Flur.ee数据库。它具有“时间旅行”功能,使您可以从任意时间点查询数据。我猜测它应该能够与“分支”模型一起工作。

该数据库最近被开发用于区块链用途。由于区块链的性质,数据需要以增量方式记录,这正是git的工作方式。他们的目标是在2019年第二季度开源

因为每个Fluree数据库都是一个区块链,所以它存储了每笔交易的全部历史记录。这是区块链确保信息不变和安全的一部分

更新:还请检查Crux数据库,该数据库可以查询插入的时间范围,您可以将其视为“版本”。Crux似乎是受到高度评价的Datomic的开源实现。

Crux是一个双时态数据库,用于存储交易时间和有效的时间历史记录。从数据库创建到当前状态的全时数据库可以通过数据库状态的事务顺序进行“时间旅行”查询,而Crux还提供了离散时间段的“时间旅行”查询,而没有不必要的设计复杂性或性能影响。这意味着Crux用户可以使用过去和将来的信息填充数据库,而不管信息到达的顺序如何,并且可以对过去的记录进行更正以构建给定域的不断改进的时间模型。


2

没有原子性就无法做到这一点,而没有使用pg_dump或快照文件系统就无法获得原子性。

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


2

从本质上讲,您想要的可能是诸如Post Facto之类的东西,它可以将数据库的版本存储在数据库中。检查此演示文稿

该项目显然从来没有真正进行过,因此它可能不会立即为您提供帮助,但这是一个有趣的概念。我担心正确执行此操作将非常困难,因为即使版本1也必须正确处理所有细节,才能使人们信任他们的工作。


2

我已经发布了sqlite的工具,可以满足您的要求。它使用定制的diff驱动程序,利用sqlite项目工具“ sqldiff”,将UUID作为主键,并省略了sqlite rowid。它仍然处于Alpha状态,因此非常感谢您的反馈。

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

https://github.com/cannadayr/git-sqlite


似乎您让git按原样存储二进制数据。取而代之的是,可以使用清除/污迹过滤器来存储转储。有一些脚本可以做到这一点。
max630

1
体面的方法,除了当您比较两个数据库状态时,您要执行转储的文本比较。通过使用sqldiff作为自定义diff驱动程序,您可以获得将数据库突变为下一个状态的实际命令。
cannadayr

1

我认为X-Istence走在正确的道路上,但是您可以对该策略进行一些其他改进。首先,使用:

$pg_dump --schema ... 

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

接下来,对一组表执行数据转储,这些表包含应用程序运行所需的配置(可能应跳过用户数据等),例如表单默认值和其他数据不可由用户修改的数据。您可以使用以下方法有选择地执行此操作:

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

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

如果绝对需要将完整备份放在存储库中,请考虑在源树之外的分支中进行备份。不过,外部备份系统最好参考匹配的svn rev。

另外,我建议在二进制文件上使用文本格式转储,以进行修订(至少用于架构),因为它们更易于区分。您始终可以将其压缩以节省空间,然后再进行检入。

最后,请查看postgres备份文档(如果还没有的话)。您对备份“数据库”而不是转储的评论方式使我想知道您是否正在考虑基于文件系统的备份(有关警告,请参阅第23.2节)。


转储不只是备份吗?
哈森

是的,但是您可以将其还原到备用数据库并在那里进行修改。
Dana the Sane

1

这个问题几乎可以回答,但我想补充一点建议来补充X-Istence和Dana the Sane的回答。

如果您需要某种程度的粒度控制(例如每天),则可以将表和模式的文本转储与rdiff-backup之类的工具结合使用,该工具可以进行增量备份。优点是,您无需存储每日备份的快照,而只需存储与前一天的差异。

这样,您既可以拥有版本控制的优势,又不会浪费太多空间。

无论如何,直接在经常更改的大型平面文件上直接使用git并不是一个好的解决方案。如果您的数据库太大,git在管理文件时将开始出现一些问题。


1

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

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

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

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

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

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

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

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

因此,从理论上讲(这就是我要寻找的东西),您可以在每次更改后转储数据库模式(通常,conjob,git钩子(也许在提交之前))(并且仅在某些非常特殊的情况下,创建更新脚本)

之后,在您的通用updatescript中(对于特殊情况,运行常规updatescript),然后比较架构(转储和当前数据库),然后自动生成必需的ALTER语句。有一些工具可以做到这一点,但是还没有找到一个好的工具。


1

我建议使用neXtep来控制数据库的版本,它具有一组不错的文档和论坛,解释了如何安装以及遇到的错误。我已经针对postgreSQL 9.1和9.3进行了测试,我能够使其在9.1上运行,但对于9.3则似乎不起作用。


@Nickolay是的,它似乎已经停产了。另外,为什么不尝试Skitch,您会在这里找到sqitch.org
Jerry M Sunny

谢谢,将检查出来!
Nickolay

1

我在个人项目中所做的是,将整个数据库存储到保管箱,然后指向MAMP,WAMP工作流以从那里直接使用它。这样,在需要进行某些开发的地方,数据库始终是最新的。但这只是为了开发!现场站点为此使用了自己的服务器!:)


1

在git版本控制下存储每个级别的数据库更改,就像在每次提交时推送整个数据库,在每次拉取时还原整个数据库一样。如果您的数据库很容易发生重大更改,而您又负担不起松动它们,则只需更新pre_commitpost_merge挂钩即可。我对我的一个项目也做过同样的事情,您可以在这里找到指导。


1

我就是这样的:

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

创建一个具有适合您实际分支的架构的模板数据库,并将其存储在存储库中。

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

这样,您可以将数据库模式置于没有数据的版本控制下。而且,如果您更改架构,则只需更改模板数据库


1

我们曾经在标准的LAMP配置上运行社交网站。我们有一个实时服务器,测试服务器和开发服务器,以及本地开发人员计算机。全部使用GIT进行管理。

在每台计算机上,我们都有PHP文件,还有MySQL服务,以及一个包含用户可以上传的图片的文件夹。Live服务器增长为拥有约100K(!)的经常用户,转储约为2GB(!),Image文件夹约为50GB(!)。到我离开时,我们的服务器已达到其CPU,Ram的极限,并且最重要的是达到了并发网络连接的极限(我们甚至编译了自己的网卡驱动程序版本,以最大限度地提高服务器的“笑声”)。我们不能(也不应该在您的网站上假设)将2GB的数据和50GB的图像放入GIT。

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

当发布时,管理员登录到开发服务器,将实时分支与所有开发人员以及开发机器上所需的分支合并到更新分支,然后将其推送到测试服务器。在测试服务器上,他检查Live服务器的更新过程是否仍然有效,并迅速将Apache中的所有流量都指向一个占位符站点,创建一个数据库转储,将工作目录从“ live”指向“ update”。 ',将所有新的sql文件执行到mysql中,并将流量重新指向正确的站点。当所有利益相关者在检查测试服务器后都同意时,管理员从测试服务器到实时服务器都执行相同的操作。之后,他将生产服务器上的实时分支合并到所有服务器的主分支中,并重新建立所有实时分支的基础。

如果测试服务器上有问题,例如。合并有太多冲突,然后恢复了代码(将工作分支指向“实时”)并且从未执行过sql文件。在执行sql文件的那一刻,这在当时被认为是不可逆的操作。如果SQL文件不能正常工作,则使用转储恢复数据库(开发人员告知,由于提供了未经测试的SQL文件)。

今天,我们维护一个sql-up和sql-down文件夹,并使用相同的文件名,开发人员必须在其中测试是否可以同时降级两个升级的sql文件。最终可以使用bash脚本执行此操作,但是如果人的眼睛一直在监视升级过程,则是个好主意。

它不是很好,但是很容易管理。希望这能为您提供一个真实,实用,相对高可用性的站点的信息。有点过时了,但仍然遵循。


0

使用iBatis Migrations之类的工具(手动简短的视频教程),该工具可让您在项目的整个生命周期中对数据库所做的更改进行版本控制,而不是对数据库本身进行版本控制。

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


0

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

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


2
您真的应该看看klonio,它是为版本控制数据库量身定制的(当前支持Mongo和MySQL)。仍处于测试阶段,但看起来很有希望。
farthVader

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.