繁体   English   中英

MongoDB / NoSQL:保持文档更改历史记录

[英]MongoDB/NoSQL: Keeping Document Change History

数据库应用程序中相当普遍的要求是跟踪数据库中一个或多个特定实体的更改。 我听说这称为行版本控制,日志表或历史表(我确定还有其他名称)。 在RDBMS中有许多方法可以处理它 - 您可以将所有源表中的所有更改写入单个表(更多日志),或者为每个源表创建单独的历史表。 您还可以选择管理应用程序代码中的日志记录或通过数据库触发器。

我正在尝试思考在NoSQL /文档数据库(特别是MongoDB)中对同一问题的解决方案是什么样的,以及如何以统一的方式解决它。 它是否像为文档创建版本号一样简单,从不覆盖它们? 为“真实”与“已记录”文档创建单独的集合? 这将如何影响查询和性能?

无论如何,这是NoSQL数据库的常见场景,如果是这样,是否有一个通用的解决方案?

好问题,我自己也在研究这个问题。

在每次更改时创建新版本

我遇到了Ruby的Mongoid驱动程序的Versioning模块 我自己没有使用它,但从我能找到的 ,它为每个文档添加了一个版本号。 旧版本嵌入在文档中。 主要缺点是每次更改都会复制整个文档 ,这会导致在处理大型文档时存储大量重复内容。 当您处理小型文档和/或不经常更新文档时,这种方法很好。

仅存储新版本中的更改

另一种方法是仅在新版本中存储已更改的字段 然后,您可以“展平”您的历史记录以重建文档的任何版本。 但这非常复杂,因为您需要跟踪模型中的更改并以应用程序可以重建最新文档的方式存储更新和删除。 这可能很棘手,因为您处理结构化文档而不是平面SQL表。

在文档中存储更改

每个字段也可以具有单独的历史记录。 通过这种方式将文档重建为给定版本更加容易。 在您的应用程序中,您不必显式跟踪更改,而只需在更改其值时创建属性的新版本。 文档看起来像这样:

{
  _id: "4c6b9456f61f000000007ba6"
  title: [
    { version: 1, value: "Hello world" },
    { version: 6, value: "Foo" }
  ],
  body: [
    { version: 1, value: "Is this thing on?" },
    { version: 2, value: "What should I write?" },
    { version: 6, value: "This is the new body" }
  ],
  tags: [
    { version: 1, value: [ "test", "trivial" ] },
    { version: 6, value: [ "foo", "test" ] }
  ],
  comments: [
    {
      author: "joe", // Unversioned field
      body: [
        { version: 3, value: "Something cool" }
      ]
    },
    {
      author: "xxx",
      body: [
        { version: 4, value: "Spam" },
        { version: 5, deleted: true }
      ]
    },
    {
      author: "jim",
      body: [
        { version: 7, value: "Not bad" },
        { version: 8, value: "Not bad at all" }
      ]
    }
  ]
}

将文档的一部分标记为在版本中删除仍然有些尴尬。 您可以为可以从应用程序中删除/恢复的部件引入state字段:

{
  author: "xxx",
  body: [
    { version: 4, value: "Spam" }
  ],
  state: [
    { version: 4, deleted: false },
    { version: 5, deleted: true }
  ]
}

使用这些方法中的每一种,您都可以在一个集合中存储最新的扁平版本,并将历史数据存储在单独的集合中。 如果您只对文档的最新版本感兴趣,这应该可以缩短查询时间。 但是当您需要最新版本和历史数据时,您需要执行两个查询,而不是一个。 因此,使用单个集合与两个单独集合的选择应取决于应用程序需要历史版本的频率

这个答案的大部分只是我脑子里的想法,我还没有真正尝试过。 回顾它,第一个选项可能是最简单和最好的解决方案,除非重复数据的开销对您的应用程序非常重要。 第二种选择非常复杂,可能不值得努力。 第三个选项基本上是选项二的优化,应该更容易实现,但可能不值得实现,除非你真的不能选择第一个。

期待对此的反馈,以及其他人对问题的解决方案:)

我们在我们的网站上部分实现了这一点,我们使用“在单独的文档中存储修订”(和单独的数据库)。我们编写了一个自定义函数来返回差异,我们存储它。不那么难,可以允许自动恢复。

为什么商店中的变体不会在文档中发生变化

而不是针对每个密钥对存储版本,文档中的当前密钥对总是表示最近的状态,并且变化的“日志”存储在历史数组中。 只有那些自创建以来已更改的键将在日志中有一个条目。

{
  _id: "4c6b9456f61f000000007ba6"
  title: "Bar",
  body: "Is this thing on?",
  tags: [ "test", "trivial" ],
  comments: [
    { key: 1, author: "joe", body: "Something cool" },
    { key: 2, author: "xxx", body: "Spam", deleted: true },
    { key: 3, author: "jim", body: "Not bad at all" }
  ],
  history: [
    { 
      who: "joe",
      when: 20160101,
      what: { title: "Foo", body: "What should I write?" }
    },
    { 
      who: "jim",
      when: 20160105,
      what: { tags: ["test", "test2"], comments: { key: 3, body: "Not baaad at all" }
    }
  ]
}

可以拥有当前的NoSQL数据库和历史NoSQL数据库。 每天都会有一个夜间ETL。 此ETL将使用时间戳记录每个值,因此它将始终是元组(版本化字段)而不是值。 如果对当前值进行了更改,它将仅记录新值,从而节省了流程中的空间。 例如,这个历史NoSQL数据库json文件可能如下所示:

{
  _id: "4c6b9456f61f000000007ba6"
  title: [
    { date: 20160101, value: "Hello world" },
    { date: 20160202, value: "Foo" }
  ],
  body: [
    { date: 20160101, value: "Is this thing on?" },
    { date: 20160102, value: "What should I write?" },
    { date: 20160202, value: "This is the new body" }
  ],
  tags: [
    { date: 20160101, value: [ "test", "trivial" ] },
    { date: 20160102, value: [ "foo", "test" ] }
  ],
  comments: [
    {
      author: "joe", // Unversioned field
      body: [
        { date: 20160301, value: "Something cool" }
      ]
    },
    {
      author: "xxx",
      body: [
        { date: 20160101, value: "Spam" },
        { date: 20160102, deleted: true }
      ]
    },
    {
      author: "jim",
      body: [
        { date: 20160101, value: "Not bad" },
        { date: 20160102, value: "Not bad at all" }
      ]
    }
  ]
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM