MongoDB/ NoSQL: Сохранение Истории Изменений Документов

довольно распространенным требованием в приложениях баз данных является отслеживание изменений одной или нескольких конкретных сущностей в базе данных. Я слышал, что это называется версиями строк, таблицей журналов или таблицей истории (я уверен, что для нее есть другие имена). Существует несколько способов подойти к нему в СУБД-вы можете записать все изменения из всех исходных таблиц в одну таблицу (больше журнала) или иметь отдельную таблицу истории для каждой исходной таблицы. У вас также есть возможность управлять записью в код приложения или через триггеры базы данных.

Я пытаюсь понять, как будет выглядеть решение той же проблемы в базе данных NoSQL/document (в частности, MongoDB) и как оно будет решаться единообразным способом. Будет ли это так же просто, как создавать номера версий для документов и никогда не перезаписывать их? Создание отдельных коллекций для" реальных "и" зарегистрированных " документов? Как это повлияет на запросы и производительность?

в любом случае, это обычный сценарий с базами данных NoSQL, и если да, то есть ли общее решение?

4 ответов


хороший вопрос, я тоже занимался этим.

создать новую версию на каждом изменении

я наткнулся на модуль управления версиями монгольского водителя для Руби. Я сам им не пользовался, но от что я мог найти, он добавляет номер версии каждого документа. Более старые версии встроены в сам документ. Основным недостатком является то, что весь документ дублируется при каждом изменении, что приведет в большом количестве дубликатов контента, хранящихся при работе с большими документами. Этот подход хорош, хотя, когда вы имеете дело с документами небольшого размера и/или не обновляете документы очень часто.

только хранить изменения в новой версии

другой подход -хранить только измененные поля в новой версии. Затем вы можете "сгладить" свою историю, чтобы восстановить любую версию документа. Это довольно сложно, хотя, как вам нужно отслеживание изменений в модели и сохранение обновлений и удалений таким образом, чтобы приложение могло восстановить обновленный документ. Это может быть сложно, поскольку вы имеете дело со структурированными документами, а не с плоскими таблицами 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 }
  ]
}

С каждым из этих подходов вы можете хранить актуальную и сплющенную версию в одной коллекции и данные истории в отдельной коллекции. Это должно улучшить время запроса, если вас интересует только последняя версия документа. Но когда вам нужны как последняя версия, так и исторические данные, вам нужно будет выполнить два запроса, а не один. Таким образом, выбор использования одной коллекции против двух отдельных коллекций должен зависеть от как часто ваше приложение нуждается в исторических версий.

большая часть этого ответа-просто свалка моих мыслей, я еще не пробовал ничего из этого. Оглядываясь назад, первый вариант, вероятно, является самым простым и лучшим решением, если только накладные расходы на дублирование данных не очень значительны для вашего приложения. Второй вариант довольно сложный и, вероятно, не стоит усилий. Третий вариант-это в основном оптимизация варианта два и должен быть проще в реализации, но, вероятно, не стоит усилий по реализации, если вы действительно не можете пойти с вариантом один.

С нетерпением ждем отзывов об этом, а также решений других людей для этой проблемы :)


мы частично реализовали это на нашем сайте, и мы используем" хранить редакции в отдельном документе " (и отдельной базе данных). Мы написали пользовательскую функцию для возврата различий, и мы храним это. Не так сложно и может позволить автоматическое восстановление.


можно иметь текущую базу данных NoSQL и историческую базу данных NoSQL. Там будет ночной ETL побежал каждый день. Этот ETL будет записывать каждое значение с меткой времени, поэтому вместо значений всегда будут кортежи (версионные поля). Он будет записывать новое значение только в случае изменения текущего значения, экономя пространство в процессе. Например, этот исторический файл JSON базы данных NoSQL может выглядеть так:

{
  _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" }
      ]
    }
  ]
}

почему бы не вариант на Сохранить изменения в документе ?

вместо хранения версий для каждой пары ключей текущие пары ключей в документе всегда представляют самое последнее состояние, а "журнал" изменений хранится в массиве истории. Только те ключи, которые изменились с момента создания, будут иметь запись в журнале.

{
  _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" }
    }
  ]
}