[mongodb] MongoDB / NoSQL : 문서 변경 기록 유지

데이터베이스 응용 프로그램에서 매우 일반적인 요구 사항은 데이터베이스에서 하나 이상의 특정 엔터티에 대한 변경 내용을 추적하는 것입니다. 이것을 행 버전 관리, 로그 테이블 또는 기록 테이블이라고 들었습니다 (다른 이름이 있다고 확신합니다). RDBMS에서 여러 가지 방법으로 접근 할 수 있습니다. 모든 소스 테이블의 모든 변경 사항을 단일 테이블 (더 많은 로그)에 쓰거나 각 소스 테이블에 대해 별도의 히스토리 테이블을 가질 수 있습니다. 응용 프로그램 코드 또는 데이터베이스 트리거를 통해 로깅을 관리하는 옵션도 있습니다.

NoSQL / 문서 데이터베이스 (특히 MongoDB)에서 동일한 문제에 대한 솔루션이 어떻게 보이는지, 어떻게 균일하게 해결되는지 생각하려고합니다. 문서의 버전 번호를 작성하고 덮어 쓰지 않는 것만 큼 간단합니까? “실제”문서와 “로그 된”문서에 대한 별도의 컬렉션을 만드시겠습니까? 이것이 쿼리 및 성능에 어떤 영향을 미칩니 까?

어쨌든, 이것은 NoSQL 데이터베이스에서 일반적인 시나리오입니까? 그렇다면 일반적인 해결책이 있습니까?



답변

좋은 질문입니다. 나는 이것도 직접 조사하고있었습니다.

변경 될 때마다 새 버전을 만듭니다.

Ruby 용 Mongoid 드라이버 의 버전 관리 모듈 을 발견했습니다. 나는 그것을 직접 사용하지는 않았지만 찾을 수있는 것에서 각 문서에 버전 번호를 추가합니다. 이전 버전은 문서 자체에 포함되어 있습니다. 가장 큰 단점은 변경때마다 전체 문서가 복제 되어 큰 문서를 처리 할 때 많은 중복 컨텐츠가 저장된다는 것입니다. 이 방법은 작은 크기의 문서를 처리하거나 문서를 자주 업데이트하지 않는 경우에 좋습니다.

변경 사항을 새 버전으로 만 저장

또 다른 방법은 변경된 필드 만 새 버전 으로 저장 하는 것 입니다. 그런 다음 내역을 ‘평평하게’하여 모든 버전의 문서를 재구성 할 수 있습니다. 그러나 모델의 변경 사항을 추적하고 응용 프로그램이 최신 문서를 재구성 할 수있는 방식으로 업데이트 및 삭제를 저장해야하기 때문에 다소 복잡합니다. 플랫 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 }
  ]
}

이러한 각 접근 방식을 사용하면 하나의 컬렉션에 최신의 병합 된 버전을 저장하고 별도의 컬렉션에 기록 데이터를 저장할 수 있습니다. 최신 버전의 문서에만 관심이있는 경우 쿼리 시간이 향상됩니다. 그러나 최신 버전과 기록 데이터가 모두 필요한 경우 하나가 아닌 두 개의 쿼리를 수행해야합니다. 따라서 단일 컬렉션과 두 개의 개별 컬렉션 중 어느 것을 사용할지는 응용 프로그램에 이전 버전이 필요한 빈도에 따라 달라집니다 .

이 답변의 대부분은 내 생각의 두뇌 덤프 일뿐입니다. 실제로는 아직 시도하지 않았습니다. 이를 되돌아 보면, 첫 번째 옵션은 아마도 중복 데이터의 오버 헤드가 애플리케이션에 매우 중요하지 않은 한 가장 쉽고 최상의 솔루션 일 것입니다. 두 번째 옵션은 매우 복잡하며 아마도 노력할 가치가 없습니다. 세 번째 옵션은 기본적으로 옵션 2의 최적화이며 구현하기가 쉽지만 실제로 옵션 1을 사용할 수 없다면 구현 노력의 가치가 없습니다.

이 문제에 대한 피드백과 다른 사람들의 문제에 대한 해결책을 기대합니다 🙂


답변

우리는 이것을 사이트에서 부분적으로 구현했으며 ‘개정 문서를 별도의 문서에 저장합니다'(및 별도의 데이터베이스)를 사용합니다. 우리는 diff를 반환하고 저장하기위한 사용자 지정 함수를 작성했습니다.


답변

문서 내에서 변경 사항저장 하지 않는 이유는 무엇 입니까?

각 키 페어에 대해 버전을 저장하는 대신 문서의 현재 키 페어는 항상 최신 상태를 나타내며 변경 기록은 기록 배열 내에 저장됩니다. 작성 이후 변경된 키만 로그에 항목을 갖습니다.

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


답변

Python 사용자 (python 3 이상) 는 pymongo의 Collection 객체의 확장 인 HistoricalCollection 이 있습니다.

문서의 예 :

from historical_collection.historical import HistoricalCollection
from pymongo import MongoClient
class Users(HistoricalCollection):
    PK_FIELDS = ['username', ]  # <<= This is the only requirement

# ...

users = Users(database=db)

users.patch_one({"username": "darth_later", "email": "darthlater@example.com"})
users.patch_one({"username": "darth_later", "email": "darthlater@example.com", "laser_sword_color": "red"})

list(users.revisions({"username": "darth_later"}))

# [{'_id': ObjectId('5d98c3385d8edadaf0bb845b'),
#   'username': 'darth_later',
#   'email': 'darthlater@example.com',
#   '_revision_metadata': None},
#  {'_id': ObjectId('5d98c3385d8edadaf0bb845b'),
#   'username': 'darth_later',
#   'email': 'darthlater@example.com',
#   '_revision_metadata': None,
#   'laser_sword_color': 'red'}]

전체 공개, 저는 패키지 작성자입니다. 🙂


답변