데이터베이스 응용 프로그램에서 매우 일반적인 요구 사항은 데이터베이스에서 하나 이상의 특정 엔터티에 대한 변경 내용을 추적하는 것입니다. 이것을 행 버전 관리, 로그 테이블 또는 기록 테이블이라고 들었습니다 (다른 이름이 있다고 확신합니다). 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'}]
전체 공개, 저는 패키지 작성자입니다. 🙂