
다중 사용자 테스트
의 데이비드 글래서 Meteor 쓴 몽고DB 쿼리에서 일치하는 문서가 누락된 블로그 문제 문제가 발생했습니다. MongoDB MMAPv1과 MongoDB WiredTiger 엔진 모두에서 이 문제를 재현하는 것은 간단합니다. 그의 글에서 얻은 결론은 다음과 같습니다(강조는 제 의견입니다).
간단히 말해서...
-
이 문제는 ID로 문서를 조회하는 쿼리와 같이 인덱스를 사용하지 않는 쿼리에는 영향을 미치지 않습니다.
-
인덱스 키에 사용된 모든 필드에 대해 명시적으로 단일 값 동일성 일치를 수행하는 쿼리에는 영향을 미치지 않습니다.
-
문서가 처음 삽입된 후 필드가 수정되지 않는 인덱스를 사용하는 쿼리에는 영향을 미치지 않습니다.
-
하지만 다른 종류의 몽고DB 쿼리는 일치하는 모든 문서를 포함하지 못할 수 있습니다!
이를 바라보는 또 다른 방법이 있습니다. 몽고DB에서 동시 작업이 진행 중일 때 쿼리가 보조 인덱스(_id가 아닌 다른 인덱스)를 사용하여 두 개의 문서를 검색할 수 있다면 결과가 잘못될 수 있습니다. 이는 많은 데이터베이스 애플리케이션에서 흔히 볼 수 있는 시나리오입니다.
테스트는 다음과 같습니다:
-
컨테이너를 만듭니다: 버킷, 테이블 또는 컬렉션.
-
30만 개의 문서와 같은 작은 데이터 집합으로 데이터를 로드합니다.
-
필터링하려는 필드(술어)에 인덱스를 만듭니다.
-
한 세션에서 인덱싱된 필드를 업데이트하고 다른 세션에서 쿼리합니다.
MongoDB 테스트
MongoDB에서 문제를 재현하는 단계:
-
MongoDB 3.2를 설치합니다.
-
MMAPv1 또는 WiredTiger로 몽고드를 불러옵니다.
-
tpcc.py를 사용하여 데이터 로드
-
python tpcc.py -warehouses 1 -no-execute mongodb
-
카운트 가져오기
> tpcc 사용
> db.ORDER_LINE.find().count();
299890
-
db.ORDER_LINE.ensureIndex({state:1});
MongoDB 실험 1: 더 높은 값으로 업데이트하기
상태 필드를 aaaaaa 값으로 설정한 다음 이 값을 zzzzzz로 동시에 업데이트하고 필드와 일치하는 두 개의 값['aaaaaa','zzzzzz']을 가진 총 문서 수를 쿼리합니다. 인덱싱된 필드의 값이 낮은 값(aaaaaa)에서 높은 값(zzzzzz)으로 이동하면 이러한 항목은 B-트리의 한쪽에서 다른 쪽으로 이동합니다. 이제 스캔이 중복된 값을 반환하는지, 더 높은 count() 값으로 변환되는지 확인하려고 합니다.
> db.ORDER_LINE.update({OL_DIST_INFO:{$gt:""}}, {$set: {state:"aaaaaa"}}, {multi:true});
WriteResult({ "nMatched" : 299890, "nUpserted" : 0, "nModified" : 299890 })
> db.ORDER_LINE.find({state:{$in:['aaaaaa','zzzzzz']}}).count();
299890
> db.ORDER_LINE.find({state:{$in:['aaaaaa','zzzzzz']}}).explain();
{
"queryPlanner" : {
"plannerVersion" : 1,
"네임스페이스" : "tpcc.ORDER_LINE",
"indexFilterSet" : false,
"parsedQuery" : {
"state" : {
"$in" : [
"아아아아아",
"zzzzzz"
]
}
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"state" : 1
},
"indexName" : "state_1",
"isMultiKey" : false,
"방향" : "앞으로",
"indexBounds" : {
"state" : [
"["아아아아아", "아아아아아"]",
"["쯔쯔", "쯔쯔"]"
]
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"호스트" : "케샤브-맥북-프로-로컬",
"port" : 27017,
"version" : "3.0.2",
"gitVersion" : "6201872043ecbbc0a4cc169b5482dcf385fc464f"
},
"OK" : 1
}
-
업데이트 문 1: 모든 문서를 상태 = "zzzzzz"로 설정하도록 업데이트합니다.
db.ORDER_LINE.update({OL_DIST_INFO:{$gt:""}}),
{$set: {상태: "zzzzzz"}}, {multi:true});
-
업데이트 문 2: 모든 문서를 상태 = "aaaaaa"로 설정하도록 업데이트합니다.
db.ORDER_LINE.update({OL_DIST_INFO:{$gt:""}}),
{$set: {상태: "aaaaaa"}}, {multi:true});
3. 카운트 문: Count documents:(state in ["aaaaaa", "zzzzzz"])
db.ORDER_LINE.find({state:{$in:['aaaaaa','zzzzzz']}}).count();
|
시간 |
세션 1: 이슈 업데이트 성명서1 (업데이트 상태 = "zzzzz") |
세션 2: 이슈 카운트 명세서를 계속 발행합니다. |
|
T0 |
업데이트 문 시작 |
Count = 299890 |
|
T1 |
업데이트 성명 계속 |
Count = 312736 |
|
T2 |
업데이트 성명 계속 |
Count = 312730 |
|
T3 |
업데이트 성명 계속 |
Count = 312778 |
|
T4 |
업데이트 성명 계속 |
Count = 312656 |
|
T4 |
업데이트 성명 계속 |
Count = 313514 |
|
T4 |
업데이트 성명 계속 |
Count = 303116 |
|
T4 |
업데이트 내역 완료 |
Count = 299890 |
결과: 이 시나리오에서는 인덱스가 많은 문서를 이중으로 계산하여 실제보다 더 많은 문서를 보고합니다.
원인: B-Tree의 리프 레벨에 있는 데이터가 정렬됩니다. B-Tree가 aaaaaa에서 zzzzz로 업데이트되면 아래쪽 끝에 있는 키가 위쪽 끝으로 이동합니다. 동시 스캔은 이 이동을 인식하지 못합니다. MongoDB는 안정적인 스캔을 구현하지 않고 항목이 들어오는 대로 계산합니다. 따라서 많은 업데이트가 진행되는 프로덕션 시스템에서는 동일한 문서를 두 번, 세 번 또는 그 이상으로 계산할 수 있습니다. 이는 동시 작업에 따라 달라집니다.
몽고DB 실험 2: 낮은 값으로 업데이트하기
역방향 작업을 수행하여 데이터를 'zzzzzz'에서 'aaaaaa'로 업데이트해 보겠습니다. 이 경우 인덱스 항목이 더 높은 값에서 더 낮은 값으로 이동하므로 스캔에서 일부 적격 문서가 누락되어 과소 계수된 것으로 표시됩니다.
|
시간 |
세션 1: 이슈 업데이트 성명서2 (업데이트 상태 = "아아아아아") |
세션 2: 이슈 카운트 명세서를 계속 발행합니다. |
|
T0 |
업데이트 문 시작 |
Count = 299890 |
|
T1 |
업데이트 성명 계속 |
Count = 299728 |
|
T2 |
업데이트 성명 계속 |
Count = 299750 |
|
T3 |
업데이트 성명 계속 |
Count = 299780 |
|
T4 |
업데이트 성명 계속 |
Count = 299761 |
|
T4 |
업데이트 성명 계속 |
Count = 299777 |
|
T4 |
업데이트 성명 계속 |
Count = 299815 |
|
T4 |
업데이트 내역 완료 |
Count = 299890 |
결과: 이 시나리오에서는 인덱스가 많은 문서를 누락하고 실제보다 적은 수의 문서를 보고합니다.
원인: 역효과가 발생합니다. 값이 zzzzzz인 키가 aaaaaa로 수정되면 항목이 B-Tree의 상위 끝에서 하위 끝으로 이동합니다. 다시 말하지만, 스캔의 안정성이 없기 때문에 상위 끝에서 하위 끝으로 이동한 키를 놓칠 수 있습니다.
MongoDB 실험 3: 동시 업데이트
두 세션이 인덱싱된 필드를 동시에 지속적으로 업데이트합니다. 이 경우, 이전 관찰에 따르면 각 세션에서 오버카운트 및 언더카운트 문제가 모두 발생합니다. MongoDB는 값을 변경한 업데이트만 보고하기 때문에 nModified 결과가 달라집니다.
그러나 수정된 문서의 총 개수는 299980개를 넘지 않습니다. 따라서 MongoDB는 동일한 문서를 두 번 업데이트하는 것을 방지하여 고전적인 할로윈 문제. 안정적인 스캔이 없기 때문에 이 다중 업데이트 문에서 업데이트된 objectID 목록을 유지 관리하고 동일한 객체가 적격 문서로 나타나면 업데이트를 피하는 방식으로 이 문제를 처리하는 것으로 추정됩니다.
세션 1
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"aaaaaa"}}, {multi:true});
WriteResult({ "nMatched" : 299890, "nUpserted" : 0, "nModified" : 299890 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"zzzzzz"}}, {multi:true});
WriteResult({ "nMatched" : 303648, "nUpserted" : 0, "nModified" : 12026 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"aaaaaa"}}, {multi:true});
WriteResult({ "nMatched" : 194732, "nUpserted" : 0, "nModified" : 138784 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"zzzzzz"}}, {multi:true});
WriteResult({ "nMatched" : 334134, "nUpserted" : 0, "nModified" : 153625 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"aaaaaa"}}, {multi:true});
WriteResult({ "nMatched" : 184379, "nUpserted" : 0, "nModified" : 146318 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"zzzzzz"}}, {multi:true});
WriteResult({ "nMatched" : 335613, "nUpserted" : 0, "nModified" : 153403 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"aaaaaa"}}, {multi:true});
WriteResult({ "nMatched" : 183559, "nUpserted" : 0, "nModified" : 146026 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"zzzzzz"}}, {multi:true});
WriteResult({ "nMatched" : 335238, "nUpserted" : 0, "nModified" : 149337 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"aaaaaa"}}, {multi:true});
WriteResult({ "nMatched" : 187815, "nUpserted" : 0, "nModified" : 150696 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"zzzzzz"}}, {multi:true});
WriteResult({ "nMatched" : 335394, "nUpserted" : 0, "nModified" : 154057 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"aaaaaa"}}, {multi:true});
WriteResult({ "nMatched" : 188774, "nUpserted" : 0, "nModified" : 153279 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"zzzzzz"}}, {multi:true});
WriteResult({ "nMatched" : 334408, "nUpserted" : 0, "nModified" : 155970 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"zzzzzz"}}, {multi:true});
WriteResult({ "nMatched" : 299890, "nUpserted" : 0, "nModified" : 0 })
>
세션 2:
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"zzzzzz"}}, {multi:true});
WriteResult({ "nMatched" : 302715, "nUpserted" : 0, "nModified" : 287864 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"aaaaaa"}}, {multi:true});
WriteResult({ "nMatched" : 195248, "nUpserted" : 0, "nModified" : 161106 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"zzzzzz"}}, {multi:true});
WriteResult({ "nMatched" : 335526, "nUpserted" : 0, "nModified" : 146265 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"aaaaaa"}}, {multi:true});
WriteResult({ "nMatched" : 190448, "nUpserted" : 0, "nModified" : 153572 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"zzzzzz"}}, {multi:true});
WriteResult({ "nMatched" : 336734, "nUpserted" : 0, "nModified" : 146487 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"aaaaaa"}}, {multi:true});
WriteResult({ "nMatched" : 189321, "nUpserted" : 0, "nModified" : 153864 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"zzzzzz"}}, {multi:true});
WriteResult({ "nMatched" : 334793, "nUpserted" : 0, "nModified" : 150553 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"aaaaaa"}}, {multi:true});
WriteResult({ "nMatched" : 186274, "nUpserted" : 0, "nModified" : 149194 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"zzzzzz"}}, {multi:true});
WriteResult({ "nMatched" : 336576, "nUpserted" : 0, "nModified" : 145833 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"aaaaaa"}}, {multi:true});
WriteResult({ "nMatched" : 183635, "nUpserted" : 0, "nModified" : 146611 })
> db.ORDER_LINE.update({state:{$gt:""}}, {$set: {state:"zzzzzz"}}, {multi:true});
WriteResult({ "nMatched" : 336904, "nUpserted" : 0, "nModified" : 143920 })
>
카우치베이스 테스트
-
Couchbase 4.5를 설치합니다.
-
tpcc.py를 사용하여 데이터 로드
-
python tpcc.py -warehouses 1 -no-execute n1ql
-
카운트 가져오기
> order_line에서 count(*)를 선택합니다;
300023
-
ORDER_LINE(state)에 인덱스 i1을 생성합니다;
-
UPDATE ORDER_LINE SET state = 'aaaaaa';
카우치베이스 실험 1: 더 높은 값으로 업데이트하기
초기 설정을 수행합니다.
> UPDATE ORDER_LINE SET state = 'aaaaaa' WHERE OL_DIST_INFO > "";
상태 필드(속성)의 값이 "aaaaaa"인 값이 300023인지 확인합니다.
> select count(1) a_cnt FROM ORDER_LINE where state = 'aaaaaa'
모두 통합
SELECT count(1) z_cnt FROM ORDER_LINE where state = 'zzzzzz';
"결과": [
{
"a_cnt": 300023
},
{
"z_cnt": 0
}
],
쿼리에서 인덱스 스캔이 수행되는지 확인해 보겠습니다.
EXPLAIN SELECT COUNT(1) AS totalcnt
주문_라인에서
WHERE state = 'aaaaaa' 또는 state = 'zzzzzz';
"~어린이": [
{
"1TP5운영자": "DistinctScan",
"scan": {
"1TP5운영자": "IndexScan",
"커버": [
"커버 ((ORDER_LINE.상태))”,
"커버 ((메타(ORDER_LINE).id))”
],
"index": "i2",
"index_id": "665b11a6c36d4136",
"키스페이스": "ORDER_LINE",
"네임스페이스": "기본값",
"스팬": [
{
"범위": {
"높음": [
""아아아아아""
],
"포함": 3,
"낮음": [
""아아아아아""
]
}
},
{
"범위": {
"높음": [
""zzzzz""
],
"포함": 3,
"낮음": [
""zzzzz""
]
}
}
],
"사용": "gsi"
}
},
또한 두 개의 개별 술어(state = 'aaaaaa')와 (state = 'zzzzzz')의 UNION ALL을 사용하여 인덱스 수를 효율적으로 계산할 수 있습니다.
cbq> explain select count(1) a_cnt
주문_라인에서
여기서 state = 'aaaaaa'
모두 통합
SELECT COUNT(1) Z_CNT
주문_라인에서
여기서 state = 'zzzzzz';
{
"요청ID": “ef99e374-48f5-435c-8d54-63d1acb9ad22”,
"서명": "json",
"결과": [
{
"plan": {
"1TP5운영자": "UnionAll",
"어린이": [
{
"1TP5운영자": "시퀀스",
"~어린이": [
{
"1TP5운영자": "인덱스 카운트 스캔",
"커버": [
"커버 ((ORDER_LINE.상태))”,
"커버 ((메타(ORDER_LINE).id))”
],
"index": "i2",
"index_id": "665b11a6c36d4136",
"키스페이스": "ORDER_LINE",
"네임스페이스": "기본값",
"스팬": [
{
"범위": {
"높음": [
""아아아아아""
],
"포함": 3,
"낮음": [
""아아아아아""
]
}
}
],
"사용": "gsi"
},
{
"1TP5운영자": "인덱스 카운트 프로젝트",
"결과_기간": [
{
"as": "a_cnt",
"expr": "count(1)"
}
]
}
]
},
{
"1TP5운영자": "시퀀스",
"~어린이": [
{
"1TP5운영자": "인덱스 카운트 스캔",
"커버": [
"커버 ((ORDER_LINE.상태))”,
"커버 ((메타(ORDER_LINE).id))”
],
"index": "i2",
"index_id": "665b11a6c36d4136",
"키스페이스": "ORDER_LINE",
"네임스페이스": "기본값",
"스팬": [
{
"범위": {
"높음": [
""zzzzz""
],
"포함": 3,
"낮음": [
""zzzzz""
]
}
}
],
"사용": "gsi"
},
{
"1TP5운영자": "인덱스 카운트 프로젝트",
"결과_기간": [
{
"as": "z_cnt",
"expr": "count(1)"
}
]
}
]
}
]
},
"text": "select count(1) a_cnt FROM ORDER_LINE where state = 'aaaaaa' UNION ALL select count(1) z_cnt FROM ORDER_LINE where state = 'zzzzzz'"
}
],
"상태": "성공",
"metrics": {
"elapsedTime": "2.62144ms",
"실행 시간": "2.597189ms",
"resultCount": 1,
"결과 크기": 3902
}
}
상태 필드를 aaaaaa 값으로 설정합니다. 그런 다음 이 값을 zzzzzz로 업데이트하고 두 값 중 하나를 사용하여 총 문서 수를 동시에 쿼리합니다.
세션 1: 상태 필드를 zzzzzz 값으로 업데이트하기
UPDATE ORDER_LINE SET state = 'zzzzzz' WHERE OL_DIST_INFO > "";
{ "mutationCount": 300023 }
세션 2: ORDER_LINE에서 'aaaaaa' 및 'zzzzzz'의 개수를 쿼리합니다.
|
시간 |
세션 1: 이슈 업데이트 성명서1 (업데이트 상태 = "zzzzz") |
a_cnt |
z_cnt |
합계 |
|
T0 |
업데이트 문 시작 |
300023 |
0 |
300023 |
|
T1 |
업데이트 성명 계속 |
288480 |
11543 |
300023 |
|
T2 |
업데이트 성명 계속 |
259157 |
40866 |
300023 |
|
T3 |
업데이트 성명 계속 |
197167 |
102856 |
300023 |
|
T4 |
업데이트 성명 계속 |
165449 |
134574 |
300023 |
|
T5 |
업데이트 성명 계속 |
135765 |
164258 |
300023 |
|
T6 |
업데이트 성명 계속 |
86584 |
213439 |
300023 |
|
T7 |
업데이트 내역 완료 |
0 |
300023 |
300023 |
결과: 데이터가 업데이트될 때 인덱스 업데이트가 이루어집니다. 값이 'aaaaaa'에서 'zzzzzz'로 마이그레이션될 때 이중으로 계산되지 않습니다.
카우치베이스 인덱스는 정기적으로 인덱스를 스냅샷하여 안정적인 스캔을 제공합니다. 이는 상당한 엔지니어링 노력이 필요하지만, 이번 호에서 살펴본 것처럼 극심한 동시성 상황에서도 안정적인 응답을 제공합니다.
인덱스 스캔이 검색하는 데이터는 포인트 스캔이 시작된 시점의 데이터입니다. 동시에 들어오는 후속 업데이트는 반환되지 않습니다. 이는 또 다른 수준의 안정성을 제공합니다.
각 인덱스 스캔에 대해 스캔의 안정성이 제공된다는 점에 유의하세요. 인덱스는 200밀리초마다 스냅샷을 생성합니다. 동일 또는 여러 인덱스에 대한 여러 인덱스 스캔이 포함된 장기 실행 쿼리가 있는 경우, 쿼리는 여러 스냅샷을 사용할 수 있습니다. 따라서 장기 실행 쿼리에서 서로 다른 인덱스 스캔은 각각 다른 결과를 반환합니다. 이 사용 사례는 향후 릴리즈에서 단일 쿼리의 여러 스캔에서 동일한 스냅샷을 사용하도록 개선될 예정입니다.
카우치베이스 실험 2: 더 낮은 값으로 업데이트하기
'zzzzzz'에서 'aaaaaa'로 데이터를 다시 업데이트하는 역연산을 해보겠습니다.
세션 1: 상태 필드를 값 aaaaaa로 업데이트하기
UPDATE ORDER_LINE SET state = 'aaaaaa' WHERE OL_DIST_INFO > "";
{ "mutationCount": 300023 }
세션 2: ORDER_LINE에서 'aaaaaa' 및 'zzzzzz'의 개수를 쿼리합니다.
|
시간 |
세션 1: 이슈 업데이트 성명서1 (업데이트 상태 = "아아아아아") |
a_cnt |
z_cnt |
합계 |
|
T0 |
업데이트 문 시작 |
0 |
300023 |
300023 |
|
T1 |
업데이트 성명 계속 |
28486 |
271537 |
300023 |
|
T2 |
업데이트 성명 계속 |
87919 |
212104 |
300023 |
|
T3 |
업데이트 성명 계속 |
150630 |
149393 |
300023 |
|
T4 |
업데이트 성명 계속 |
272358 |
27665 |
300023 |
|
T5 |
업데이트 성명 계속 |
299737 |
286 |
300023 |
|
T6 |
업데이트 내역 완료 |
0 |
300023 |
300023 |
카우치베이스 실험 3: 동시 업데이트
두 개의 세션이 색인된 필드를 동시에 지속적으로 업데이트합니다. 인덱스를 안정적으로 스캔하면 항상 적격 문서의 전체 목록이 반환됩니다: 30023을 반환하고 Couchbase는 모든 문서를 업데이트하고 변경 횟수를 30023으로 보고합니다. 업데이트는 이전 값이 새 값과 동일한지 여부에 관계없이 업데이트가 이루어집니다.
세션 1:
업데이트 ORDER_LINE set state = 'aaaaaa' 여기서 state > "";
{ "mutationCount": 300023 }
업데이트 ORDER_LINE set state = 'zzzzzz'여기서 state > "";
{ "mutationCount": 300023 }
업데이트 ORDER_LINE set state = 'aaaaaa' 여기서 state > "";
{ "mutationCount": 300023 }
업데이트 ORDER_LINE set state = 'zzzzzz'여기서 state > "";
{ "mutationCount": 300023 }
업데이트 ORDER_LINE set state = 'aaaaaa' 여기서 state > "";
{ "mutationCount": 300023 }
업데이트 ORDER_LINE set state = 'zzzzzz'여기서 state > "";
{ "mutationCount": 300023 }
업데이트 ORDER_LINE set state = 'aaaaaa' 여기서 state > "";
{ "mutationCount": 300023 }
업데이트 ORDER_LINE set state = 'zzzzzz'여기서 state > "";
{ "mutationCount": 300023 }
세션 2:
업데이트 ORDER_LINE set state = 'zzzzzz'여기서 state > "";
{ "mutationCount": 300023 }
업데이트 ORDER_LINE set state = 'aaaaaa' 여기서 state > "";
{ "mutationCount": 300023 }
업데이트 ORDER_LINE set state = 'zzzzzz'여기서 state > "";
{ "mutationCount": 300023 }
업데이트 ORDER_LINE set state = 'aaaaaa' 여기서 state > "";
{ "mutationCount": 300023 }
업데이트 ORDER_LINE set state = 'zzzzzz'여기서 state > "";
{ "mutationCount": 300023 }
업데이트 ORDER_LINE set state = 'aaaaaa' 여기서 state > "";
{ "mutationCount": 300023 }
업데이트 ORDER_LINE set state = 'zzzzzz'여기서 state > "";
{ "mutationCount": 300023 }
결론
MongoDB:
-
인덱스의 한 부분에서 다른 부분(상위에서 하위)으로 데이터를 이동하는 동시 업데이트가 있는 경우 MongoDB 쿼리는 문서를 놓치게 됩니다.
-
인덱스의 한 부분에서 다른 부분(하위에서 상위)으로 데이터를 이동하는 동시 업데이트가 있는 경우, MongoDB 쿼리는 동일한 문서를 여러 번 반환합니다.
-
MongoDB에서 동시에 여러 번 업데이트하면 동일한 문제가 발생하여 필요한 모든 문서를 업데이트하지 못할 수 있지만, 한 문장에서 동일한 문서를 두 번 업데이트하는 것은 방지할 수 있습니다.
-
MongoDB를 사용하는 애플리케이션을 개발할 때는 각 쿼리에 대해 하나의 문서만 선택하고 업데이트하도록 데이터 모델을 설계해야 합니다. 동시 업데이트가 있을 경우 잘못된 결과가 반환되므로 MongoDB에서 다중 문서 쿼리는 피하세요.
카우치베이스:
-
Couchbase는 동시 업데이트가 있는 경우에도 예상되는 적격 문서 수를 반환합니다. Couchbase의 안정적인 인덱스 스캔은 MongoDB가 제공하지 않는 애플리케이션에 대한 보호 기능을 제공합니다.
-
동시 업데이트는 안정적인 인덱스 스캔의 이점을 누리며 신청서가 발행될 때 모든 적격 서류를 처리합니다.
감사
이 작업에 대한 검토와 귀중한 의견을 제공해 주신 Couchbase 인덱싱 팀의 Sriram Melkote와 Deepkaran Salooja에게 감사드립니다.
참조
-
MongoDB 강력한 일관성: https://www.mongodb.com/nosql-explained
-
MongoDB 쿼리가 항상 일치하는 모든 문서를 반환하는 것은 아닙니다! https://engineering.meteor.com/mongodb-queries-dont-always-return-all-matching-documents-654b6594a827#.s9y0yheuv
-
카우치베이스: https://www.couchbase.com