使用git repository作为数据库后端

时间:2013-11-22 17:41:49

标签: database git database-performance database-replication document-database

我正在做一个处理结构化文档数据库的项目。我有一个类别树(约1000个类别,每个级别最多约50个类别),每个类别包含数千个(最多,比如说,~10000)结构化文档。每个文档都是几千字节的数据,采用某种结构化形式(我更喜欢YAML,但它也可能是JSON或XML)。

此系统的用户可以执行多种操作:

  • 通过ID检索这些文件
  • 通过其中的一些结构化属性搜索文档
  • 编辑文件(即添加/删除/重命名/合并);应将每个编辑操作记录为具有一些注释的事务
  • 查看特定文档记录更改的历史记录(包括查看更改文档的人员,时间和原因,获取更早版本 - 如果需要,可能还原为此版本)

当然,传统的解决方案是使用某种文档数据库(例如CouchDB或Mongo)来解决这个问题 - 然而,这个版本控制(历史)的东西诱惑我一个疯狂的想法 - 为什么我不应该使用git存储库作为此应用程序的数据库后端?

乍一看,它可以像这样解决:

  • category = directory,document = file
  • 通过ID =>获取文档更改目录+读取工作副本中的文件
  • 使用编辑评论编辑文档=>由各种用户提交+存储提交消息
  • history =>正常的git日志和旧事务的检索
  • search =>这是一个稍微棘手的部分,我想它需要定期将类别导出到关系数据库中,并且我们将允许搜索的列的索引

这个解决方案还有其他常见的陷阱吗?有没有人试图实现这样的后端(即任何流行的框架--RoR,node.js,Django,CakePHP)?这个解决方案是否会对性能或可靠性产生任何影响 - 即它是否证明git比传统数据库解决方案慢得多,或者存在任何可扩展性/可靠性缺陷?我认为推送/拉取彼此存储库的这类服务器集群应该相当强大。可靠。

基本上,告诉我如果此解决方案能够正常工作,为什么它将会或不会?

4 个答案:

答案 0 :(得分:12)

确实是一种有趣的方法。我想说,如果您需要存储数据,请使用数据库,而不是源代码存储库,它是专为特定任务而设计的。如果您可以使用Git开箱即用,那么它很好,但您可能需要在其上构建文档存储库层。所以你也可以在传统的数据库上构建它,对吗?如果它是您感兴趣的内置版本控件,为什么不使用open source document repository tools之一呢?有很多可供选择。

好吧,如果你决定去Git后端,那么如果按照你的描述实现它,基本上它可以满足你的要求。但是:

1)你提到“服务器集群互相推/拉” - 我已经考虑了一段时间,但我仍然不确定。你不能推/拉几个repos作为原子操作。我想知道在并发工作期间是否有可能出现合并混乱。

2)也许您不需要它,但是您未列出的文档存储库的明显功能是访问控制。您可以通过子模块限制对某些路径(=类别)的访问,但可能您无法轻松地在文档级别授予访问权限。

答案 1 :(得分:11)

我的2便士价值。有点渴望,但......我的一个孵化项目有类似的要求。与你的类似,我的关键要求是文档数据库(在我的情况下为xml),文档版本控制。它适用于具有大量协作用例的多用户系统。我倾向于使用支持大多数关键要求的可用开源解决方案。

为了减少追逐,我找不到任何提供两者的产品,其方式足够可扩展(用户数量,使用量,存储和计算资源)。我对所有有希望的git偏向git能力和(可能的)解决方案可以用它来制造。随着我更多地使用git选项,从单一用户角度转向多(毫)用户角度成为一个明显的挑战。不幸的是,我没有像你那样做大量的性能分析。 (..懒惰/退出早......对于版本2,口头禅)给你力量!无论如何,我的偏见已经转变为下一个(仍有偏见的)替代方案:在各自的领域,数据库和版本控制中最好的工具网格化。

虽然仍在进行中(......并且略微忽略),变形版本就是这样。

    前端的
  • :( userfacing)使用第一级数据库 存储(与用户应用程序连接)
  • 在后端, 使用版本控制系统(VCS)(如git)来执行 数据库中数据对象的版本控制

本质上,它相当于向数据库中添加一个版本控制插件,使用一些集成粘合剂,您可能需要开发它,但可能要容易得多。

它(应该)如何工作是主要的多用户界面数据交换是通过数据库。 DBMS将处理所有有趣和复杂的问题,如多用户,并发e,原子操作等。在后端,VCS将对一组数据对象执行版本控制(无并发或多用户问题)。对于数据库上的每个有效事务,只对已经有效更改的数据记录执行版本控制。

对于接口胶,它将采用数据库和VCS之间简单的互通功能的形式。在设计方面,简单的方法是事件驱动的接口,数据库中的数据更新触发版本控制程序(提示:假设Mysql, use of triggers and sys_exec()等等......)。在实现复杂性方面,它将范围从简单有效(例如脚本)到复杂和精彩(一些编程的连接器接口)。一切都取决于你想要用它多疯狂,以及你愿意花多少汗水资金。我认为简单的脚本编写应该是神奇的。并且为了访问最终结果,各种数据版本,一个简单的替代方案是使用VCS中版本标记/ id / hash引用的数据填充数据库的克隆(更多是数据库结构的克隆)。再次,这个位将是一个简单的查询/翻译/地图作业的接口。

仍有一些挑战和未知因素需要处理,但我认为其中大部分的影响和相关性在很大程度上取决于您的应用程序要求和用例。有些人可能最终会成为非问题。一些问题包括2个关键模块,数据库和VCS之间的性能匹配,用于具有高频数据更新活动的应用程序,git端的资源(存储和处理能力)随时间的缩放作为数据,以及用户成长:稳定,指数或最终高原

在上面的鸡尾酒中,这是我目前正在酿造的

  • 使用Git作为VCS(由于仅使用2个版本之间的更改集或增量,最初被认为是旧的CVS)
  • 使用mysql(由于我的数据具有高度结构化的特性,xml具有严格的xml架构)
  • 使用MongoDB进行游戏(尝试使用与git中使用的本机数据库结构非常匹配的NoSQl数据库)

一些有趣的事实 - git实际上确实可以清楚地优化存储,例如压缩,以及仅修改对象之间的增量存储 - 是的,git只存储数据对象修订版之间的变更集或增量,它适用于何处(它知道何时以及如何)。参考:packfiles,深入guts of Git internals - 回顾git的对象存储(内容可寻址文件系统),显示与没有SQL数据库(如mongoDB)的相似性(从概念角度来看)。同样,以牺牲汗水资本为代价,它可能为整合2和性能调整提供更有趣的可能性

如果你做到这一点,请告诉我,如果以上内容可能适用于您的情况,并假设它将如何与您上次综合性能分析中的某些方面相符合

答案 2 :(得分:2)

正如您所提到的,多用户案例处理起来有点棘手。一种可能的解决方案是使用导致

的用户特定的Git索引文件
  • 不需要单独的工作副本(磁盘使用仅限于更改的文件)
  • 无需耗时的准备工作(按用户会话)

诀窍是将Git' GIT_INDEX_FILE环境变量与手动创建Git提交的工具结合起来:

以下是解决方案概要(命令中省略了实际的SHA1哈希值):

# Initialize the index
# N.B. Use the commit hash since refs might changed during the session.
$ GIT_INDEX_FILE=user_index_file git reset --hard <starting_commit_hash>

#
# Change data and save it to `changed_file`
#

# Save changed data to the Git object database. Returns a SHA1 hash to the blob.
$ cat changed_file | git hash-object -t blob -w --stdin
da39a3ee5e6b4b0d3255bfef95601890afd80709

# Add the changed file (using the object hash) to the user-specific index
# N.B. When adding new files, --add is required
$ GIT_INDEX_FILE=user_index_file git update-index --cacheinfo 100644 <changed_data_hash> path/to/the/changed_file

# Write the index to the object db. Returns a SHA1 hash to the tree object
$ GIT_INDEX_FILE=user_index_file git write-tree
8ea32f8432d9d4fa9f9b2b602ec7ee6c90aa2d53

# Create a commit from the tree. Returns a SHA1 hash to the commit object
# N.B. Parent commit should the same commit as in the first phase.
$ echo "User X updated their data" | git commit-tree <new_tree_hash> -p <starting_commit_hash>
3f8c225835e64314f5da40e6a568ff894886b952

# Create a ref to the new commit
git update-ref refs/heads/users/user_x_change_y <new_commit_hash>

根据您的数据,您可以使用cron作业将新引用合并到master,但冲突解决方案可以说是最难的部分。

欢迎让它变得更容易的想法。

答案 3 :(得分:2)

我在libgit2之上实现了Ruby library,这使得这很容易实现和探索。有一些明显的局限性,但它也是一个相当自由的系统,因为你得到了完整的git工具链。

该文档包含有关性能,权衡等的一些想法。