在MongoDB中实现数据版本控制的方法


298

您能否分享您的想法,如何在MongoDB中实现数据版本控制。(我已经问过有关Cassandra的类似问题。如果您有任何想法,最好是哪个数据库更好,请分享)

假设我需要在一个简单的通讯簿中对记录进行版本控制。(地址簿记录存储为平面json对象)。我希望这段历史:

  • 将很少使用
  • 将一次全部使用,以“时间机器”的方式呈现
  • 一个记录不会有超过几百个的版本。历史不会过期。

我正在考虑以下方法:

  • 创建一个新的对象集合以存储记录的历史记录或对记录的更改。它将为每个版本存储一个对象,并引用地址簿条目。这样的记录如下:

    {
     '_id':'新ID',
     “用户”:user_id,
     '时间戳':时间戳,
     'address_book_id':'通讯录记录的ID' 
     'old_record':{'first_name':'Jon','last_name':'Doe'...}
    }
    

    可以修改此方法以存储每个文档的版本数组。但这似乎是较慢的方法,没有任何优势。

  • 将版本存储为附加到地址簿条目的序列化(JSON)对象。我不确定如何将此类对象附加到MongoDB文档。也许作为字符串数组。(以使用CouchDB的简单文档版本控制为模型


1
我想知道自回答问题以来情况是否有所改变?我对oplog知之甚少,但那时是否存在,会有所作为吗?
兰迪·L

我的方法是将所有数据视为一个时间序列。

Answers:


152

深入探讨这个问题时,第一个大问题是“您如何存储变更集”

  1. 差吗?
  2. 完整记录副本?

我个人的方法是存储差异。因为这些差异的显示确实是一个特殊的动作,所以我会将差异放入另一个“历史”集合中。

我将使用其他集合来节省内存空间。通常,您不希望简单查询的完整历史记录。因此,通过将历史记录保留在对象之外,您还可以在查询该数据时将其保留在通常访问的内存之外。

为了使我的生活更轻松,我将使历史文档包含带有时间戳记的差异字典。像这样:

{
    _id : "id of address book record",
    changes : { 
                1234567 : { "city" : "Omaha", "state" : "Nebraska" },
                1234568 : { "city" : "Kansas City", "state" : "Missouri" }
               }
}

为了使我的生活变得真正轻松,我将把这一部分用于访问数据的DataObjects(EntityWrapper,无论如何)。通常,这些对象具有某种形式的历史记录,因此您可以轻松地覆盖save()方法以同时进行此更改。

更新:2015-10

看起来现在有了处理JSON差异的规范。这似乎是存储差异/更改的更可靠的方法。


2
您是否担心这样的“历史记录”文档(更改对象)会随着时间增长并且更新效率低下?还是MongoDB处理文档容易增长?
Piotr Czapla

5
看一下编辑。添加到changes真的很容易:db.hist.update({_id: ID}, {$set { changes.12345 : CHANGES } }, true)这将执行upsert,只会更改所需的数据。Mongo创建带有“缓冲区空间”的文档来处理这种类型的更改。它还监视集合中的文档如何更改并修改每个集合的缓冲区大小。因此,MongoDB正是针对这种类型的更改而设计的(添加新属性/将其推送到数组)。
盖茨副总裁,2010年

2
我已经做过一些测试,确实空间预留工作得很好。当记录重新分配到数据文件的末尾时,我无法捕获性能损失。
Piotr Czapla

4
您可以使用github.com/mirek/node-rus-diff为您的历史记录生成(与MongoDB兼容)差异。
Mirek Rusin 2014年

1
JSON补丁RFC提供了一种方式来表达difffs。它具有几种语言的实现
杰罗姆

31

有一个称为“ Vermongo”的版本控制方案,该方案解决了其他答复中未涉及的某些方面。

这些问题之一是并发更新,另一问题是删除文档。

Vermongo将完整的文档副本存储在影子集合中。在某些用例中,这可能会导致过多的开销,但是我认为这也简化了很多事情。

https://github.com/thiloplanz/v7files/wiki/Vermongo


5
您实际上如何使用它?
hadees

6
没有有关如何实际使用此项目的文档。它以某种方式与Mongo一起生活吗?它是Java库吗?这仅仅是思考问题的一种方式吗?没有想法,也没有提示。
2013年

1
这实际上是一个Java应用程序,相关代码位于此处:github.com/thiloplanz/v7files/blob/master/src/main/java/v7db/…–
ftrotter

20

这是针对当前版本和所有旧版本使用单个文档的另一种解决方案:

{
    _id: ObjectId("..."),
    data: [
        { vid: 1, content: "foo" },
        { vid: 2, content: "bar" }
    ]
}

data包含所有版本。该data数组是有序的,新版本将仅$push在该数组的末尾显示。data.vid是版本ID,是一个递增数字。

获取最新版本:

find(
    { "_id":ObjectId("...") },
    { "data":{ $slice:-1 } }
)

通过vid以下方式获取特定版本:

find(
    { "_id":ObjectId("...") },
    { "data":{ $elemMatch:{ "vid":1 } } }
)

仅返回指定的字段:

find(
    { "_id":ObjectId("...") },
    { "data":{ $elemMatch:{ "vid":1 } }, "data.content":1 }
)

插入新版本:(并防止并发插入/更新)

update(
    {
        "_id":ObjectId("..."),
        $and:[
            { "data.vid":{ $not:{ $gt:2 } } },
            { "data.vid":2 }
        ]
    },
    { $push:{ "data":{ "vid":3, "content":"baz" } } }
)

2vid当前最新版本的,3是要插入的新版本。由于您需要最新版本vid,因此很容易获得下一个版本vidnextVID = oldVID + 1

$and条件将确保,这2是最新的vid

这样,就不需要唯一索引,但是应用程序逻辑必须注意增加vid插入时的增量。

删除特定版本:

update(
    { "_id":ObjectId("...") },
    { $pull:{ "data":{ "vid":2 } } }
)

而已!

(记住每个文件16MB的限制)


使用mmapv1存储时,每次将新版本添加到数据时,都有可能会移动文档。
raok1997

恩,那就对了。但是,如果您只是偶尔添加新版本,这应该可以忽略不计。
本杰明·M


9

我研究了该解决方案,以适应数据的发布,草稿和历史版本:

{
  published: {},
  draft: {},
  history: {
    "1" : {
      metadata: <value>,
      document: {}
    },
    ...
  }
}

我在这里进一步解释该模型:http : //software.danielwatrous.com/representing-revision-data-in-mongodb/

对于那些可能在Java中实现类似代码的人,下面是一个示例:

http://software.danielwatrous.com/using-java-to-work-with-versioned-data/

如果愿意,包括所有可以分叉的代码

https://github.com/dwatrous/mongodb-revision-objects


很棒的东西:)
乔纳森(Jonathan),


4

另一种选择是使用mongoose-history插件。

let mongoose = require('mongoose');
let mongooseHistory = require('mongoose-history');
let Schema = mongoose.Schema;

let MySchema = Post = new Schema({
    title: String,
    status: Boolean
});

MySchema.plugin(mongooseHistory);
// The plugin will automatically create a new collection with the schema name + "_history".
// In this case, collection with name "my_schema_history" will be created.

1

我已经将以下软件包用于流星/ MongoDB项目,并且效果很好,主要优点是它将历史记录/修订存储在同一文档的数组中,因此无需其他出版物或中间件即可访问更改历史记录。它可以支持有限数量的先前版本(例如最后十个版本),还支持更改串联(因此,在特定时期内发生的所有更改将被一个修订版本覆盖)。

尼克洛宗/流星收集修订版

另一个声音选项是使用Meteor Vermongo(在此处

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.