Lock 매커니즘과 Transaction
- 4.0 이하에는 지원하지 않음
- Two Phase Commit
- 상태 값을 부여하여 진행하고 해당 로그를 다른곳에 저장하여 상태 검증할 수 있게 함.
- 상태 검증해서 완벽하게 끝났다면, 상태 로그값을 삭제.
- 결론은 계속 양쪽으로 상태를 체크해야함.
- MongoDB에서 명시적으로 Lock은 Global Lock(Instance를 Lock) 만 가능(3.4 에서는 Global Lock외에는 모두 묵시적 Lock만 지원) > fsyncLock 명령어 이용
- Global Lock을 사용하면 읽기는 막지 않음 / 쓰기는 Blocking 때문에 이후의 데이터 읽기나 변경이 모두 멈춤(주의)
- fsyncLock을 사용한 connection에 대해서는 닫지 말고 유지 하라고 권고(다른 connection에서 unlock명령어가 실행 안될 가능성이 존재
- Intention Lock(인덴션 락) 은 IS(Intent Shared Lock) + IX (Intent Exclusive Lock) 의도된 잠금을 묶어서 하는 말
- Intention Lock의 경우 Collection - DB - Global까지 가능 / Document의 경우 불가( 묵시적 Exclusive lock만 가능)
General Transaction
- WiredTiger Storage Engine 을 기반으로 작성
- 최고 레벨의 격리 수준은 Snapshot (=Repeatable-read) / Serializable 격리 수준은 제공하지 않음
- Transaction Log(General Log, Redo Log), CheckPoint를 이용하여 영속성(Durability) 보장
- Transaction Log가 없어도 마지막 Checkpoint 시점의 데이터를 복구 가능
- 하지만 격리 수준을 제공할 뿐 선택해서 사용 못함
- SNAPSHOT(REPEATABLE-READ)
- MongoDB Wired Storage Engine의 기본 격리수준
- Transaction을 시작한 시점 부터 commit / rollback 완료될때까지 자신이 처음 검색한 데이터를 완료될때까지 같은 결과
- 명시적 Transaction을 지원하지 않으며, 단일 Document에 대해서만 지원
- Transaction Commit과 Checkpoint 2가지 형태로 영속성(Durability) 보장
- Commit되지 않은 변경 데이터는 공유 캐시 크기보다 작아야 함
- Commit 이 되어야만 디스크로 저장하기에 Transaction 내 변경 데이터 사이즈가 공유 캐시 크기보다 작아야 함
- DML 발생 시 Lock이 발생하면 외부적으로는 기다리는 것으로 나오지만 내부 로직은 재시도를 함 (WriteConflict Exception -> Error를 발생시켜 재시도를 하는 내부 Exception처리 -> 사용자는 Waiting 한다고 생각하면 됨)
- WriteConflict Exception 작업은 cpu 사용량을 높이며, db.serverStatus() 명령으로 확인 가능
- db.serverStatus() 의 writeConflicts 수치가 증가할수록 하나의 Document에 DML시도가 많은 것이므로 로직 변경을 고려해 보자
- Find 명령어는 writeConflicts 와는 무관 (X-Lock을 걸지 않으므로)
- 여러 문장을 하나의 문장으로 명령하더라도, MongoDB 내부에서는 잘게 짤라 각각의 문장으로 처리
- db.users.inser({_id:1, name : 'ABC'},{_id:2, name:'DEF'}) -> db.users.insert({_id:1 ,name:'ABC'}) / db.users.insert({_id:2 ,name:'DEF'})
- 그렇기 때문에 insert 중간에 에러 발생한면, 에러 발생하기 직전까지는 Insert 완료, 이 후의 데이터는 실행 안됨
- db.collection.bulkWrite([],{ordered:false}) 로 하여 실행 도중 에러 발생하는 경우 데이터 저장 관련 확인하기 힘듦
- 한 문장에 여러 Document가 변경 되더라도(multi:true) 내부에서는 한건씩 update 가 진행(중간에 에러 발생해도 롤백 안됨)
- 데이터 읽기의 경우 건건이가 아닌 일정 단위로 트랜잭션을 시작하고 완료(스냅샷 유지하여 읽음)
- 일관성에 문제가 발생할 수 있음
- 스냅샷 유지 요건(해당 요건이 모두 충족되어야 스냅샷 해제 및 삭제, 새로운 스냅샷 생성)
- 쿼리가 지정된 건수의 Document를 읽은 경우 (internalQueryExecYieldIterations = 128)
- Document 128건을 읽은 경우
- 쿼리가 지정된 시간 동안 수행된 경우 (internalQueryExecYieldPeriodMS = 10)
- 10 밀리 세컨드 이상을 쿼리가 실행되는 경우
- 대량의 데이터를 읽는 경우 해당 데이터가 버퍼에 존재(스냅샷에서)하며, 그것을 가져와서 보여주기 때문에, find하는 도중에 보여준 데이터를 삭제 하더라도 그 전에 find한 데이터는 계속 보여줌
- 정렬을 사용하게 되면 정렬 버퍼에 해당 데이터가 적재된 후, 적재된 데이터를 보여줌
- 커서에 정렬된 데이터를 남겨둔 채 (모든 데이터를 클라이언트에 안보내고 중단하는 경우) 커서를 닫지 않으면, 메모리 누수가 발생 가능성(MongoDB에서 자동으로 닫기 전까지)
- 원하는 데이터를 모두 봤다면 반드시 커서를 닫아 주도록 하자
- (Transaction-level) Write Concern
- 사용자의 변경 요청에 응답하는 시점을 결정하는 옵션을 Write Concern
- DML 상황에서만 적용(Update / Delete / Insert에 대해서)
- Client, Database, Collection 레벨에서 Write Concern을 결정 가능
- 단일 노드 동기화 제어
- 4가지가 존재 옵션 존재
- UNACKNOWLEDGED
- 클라이언트가 MongoDB서버로 전송된 명령이 정상적으로 처리 됐는지 아니면 에러가 발생해서 중간에 작업이 멈췄는지 관심 없이 무시
- 에러 여부를 전혀 알수가 없음
- 실무에서는 거의 사용하지 않음
- ACKNOWLEDGED (최근 MongoDB의 Default)
- 메모리상에 변경된 데이터의 적재 여부를 통보
- 다른 Session 또는 자신의 Session에서 변경 값 조회 시 변경값 확인 가능
- 메모리에서 Disk로 적재 하기 전에 장애 발생 시 손실의 위험 존재
- JOURNALED
- General Log 까지(Disk) 작성되어야 결과 반환 (General Log 활성 여부가 먼저 선행으로 체크해야 사용 가능함 / 3.6에서 부터는 반드시 General Log가 활성화 됨-비활성화 불가)
- 장애 발생하더라도 언제든 복구가 가능
- 레플리카 셋을 사용하는 경우 단일 MongoDB서버를 사용할 때에는 발생하지 않는 새로운 문제가 발생(동기화가 안되는 경우 문제 발생 가능성을 사전에 알기 힘듦)
- 3.6에서 부터는 General Log 가 무조건 사용되기에 JOURNALED를 사용 가능하지만, ACKNOWLEDGED 모드와 차이가 없으며 JOURNALED 방식으로 작동)
- FSYNC (MMapv1)
- General Log가 없던 버전에서 사용했던 방식
- 디스크의 데이터파일까지 모두 작성된 후에 결과 반환하는 방식으로 높은 비용의 작업
- 제거가 될 기능
- 레플리카 셋 간의 동기화 제어
- Primary가 장애 발생 시 Secondary들에게 최신 OpLog를 전달하지 못한 경우 롤백되어 손실이 발생할 수 있는 점을 보완하기 위하여 설정 제어 방식
- "{w:?}" 으로 Option을 줄 수 있으며, ? 에는 숫자 또는 문자가 들어가는데 해당 값에 따라 방식이 변경
- {w:2}
- 3개의 멤버 중 2개(Primary + Secondary 1) 의 멤버가 변경 요청을 필요한 수준까지 처리 했을 때 성공, 실패 여부를 반환
- ACKNOWLEDGED + {w:2} (기본이라 생략도 가능)
- JOURNALED + {w:2}
- {w:"majority"}
- 위의 숫자를 작성하게 되면 멤버수가 변경될 때마다 변경해 줘야함(하드 코딩)
- "majority" 로 작성하게 되면 멤버수의 상관 없이 과반 수 이상일 경우 결과 반환
- Read할 때에도 멤버 서로의 OpLog의 적용 위치를 알기 때문에, majority 로 하면 OpLog가 적용이 덜 된 곳의 멤버에는 접근하지 않음
- "Tag-set name"
- 각 멤버들에게 Tag를 할당 가능하며, 해당 Tag를 가진 멤버에게 체크
- (Transaction-level) ReadConcern (https://docs.mongodb.com/manual/core/transactions/#std-label-transactions-write-concern)
- Replica 간 동기화 과정 중에 데이터 읽기를 일관성 있게 유지할 수 있도록 MongoDB서버에서 옵션으로 제공(일관성 목적)
- Option에 따라 다르겠지만, 쿼리 실행 시 기본적으로 세컨드리 멤버들이 OpLog를 원하는 옵션 수준 까지 동기화될 때까지 기다린다
- 3가지 옵션이 존재( local, majority, linearizable )하며 Client Level, Database Level, Collection Level에서 설정 가능
- Local : Default 로 가장 최신의 데이터를 반환하는 방식. 주로 Primary에서 가져가며, 장애 발생 시 해당 데이터가 롤백되어 Phantom Read가 발생 가능성 존재
- Local 을 제외한 나머지 옵션에 대해서는 기본적으로 maxTimeMS 설정을 권장
- 4.4 fCV 가 적용된 경우 Transaction 내에서 Index를 생성할 수 있으며, 명시적으로 생성하는 경우 local 로 설정하여 진행해야 함
- majority : 다수의 멤버들이 최신 데이터를 가지고 있는 경우에 읽기 결과가 반환, 모든 DB가 장애 발생 시 데이터가 롤백되어 Phantom Read가 발생 가능성이 존재하지만, 롤백으로 인해 사라지는 경우 가능성이 가장 낮음
MongoDB --enableMajorityReadConcern .........
또는 MongoDB.conf 에서
...
setParameter :
enableMajorityReadConcern: true
...
- snapshot
- linearizable : 모든 멤버가 가진 변경 사항에 대해서만 쿼리 결과 반환. 모든 DB가 장애가 발생하더라도 롤백은 일어나지 않음. 3.4부터 지원. 쿼리 응답시간이 자연스럽게 지연될 가능성이 존재. 무재한 기다릴 수 있기 때문에 쿼리 타임 아웃 설정은 필수.
- db.runCommand({ find:"users", filter: {name:"matt"}, readConcern: {level: "linearizable"}, maxTimeMS:5000 })
- Read Preference
- 클라이언트의 쿼리를 어떤 DB 서버로 요청해서 실행할 것인지 결정하는 옵션(분산 목적)
- 서버에 접속하는 시점에 설정되기에 컨넥션 생성하는 즉시 Read Preference 옵션을 설정
- Find쿼리만 영향이 미치며 5가지가 존재
- db.getMongo().setReadPref('primaryPreferred')
- db.users.find({name:"matt"}).readPref('primaryPreferred')
- Primary (Default)
- Primary로만 쿼리 실행하며, Primary가 없으면 쿼리 실행은 실패(장애 발생하고 fail over 이전)
- PrimaryPreferred
- 가능하면 Primary로 전송하며 장애로 Primary가 없는 경우 Secondary 로 요청
- Secondary
- Secondary 멤버로만 전송하며, Primary로는 요청하지 않음. 멤버가 2개 이상일 경우 적절히 분산하여 요청
- Secondary가 없는 경우 실패 발생
- secondaryPreferred
- Secondary와 동일하지만 없는 경우 Primary로 요청
- nearest
- 쿼리 요청 응답시간이 가장 빠른 멤버로 요청 (Primary, Secondary 고려 안함)
- 동일한 대역에서는 미흡하지만, 레플리카 셋이 글로벌하게 분산되어 멤버들간의 응답시간이 차이가 나는 경우 적절
- 샤딩 환경의 중복 Document 처리
- 샤딩된 클러스터는 2가지 경우에만 Document 샤드의 소유권(Ownership)을 체크
- 쿼리에 샤드의 메타 정보 버전이 포함된 경우
- 쿼리의 조건에 샤드 키가 포함된 경우
- 청크가 이동될 때마다 컨피그 서버의 메타 정보가 변경됨(메타 정보의 버전이 1씩 증가)
- 버전 정보를 쿼리 실행시마다 전달하는데, 버전이 포함된 쿼리를 'Versioned Query'라고 명칭
- 청크가 이동되더라도 한 시점의 일괄된 데이터를 보장하는 이유가 버전 정보를 포함하고 있기 때문
- 하지만 Primary에서 실행한 경우만 Versioned Query를 사용하며 Secondary에서는 버전 정보를 포함하지 않음(Unversioned Query)
- Shard key를 포함한 쿼리의 경우 특정 샤드로만 쿼리가 전달되므로 해당 샤드가 가진 청크에 포함된 Document만 반환
- Shard Key가 없으며, Secondary에서 조회하는 경우 Document의 중복이 발생 가능성 존재
[ Multi-Document Transaction]
- Mongodb Client API도 반드시 4.2 버전으로 사용
- 4.4 부터 명시적으로 Transaction 내에서 collection 을 생성 가능(Client API 또한 4.4로 사용해야 가능)
- MongoDB 버전 4.0부터 제공되었지만, Replica Sets 환경에서 만 지원되었으며 4.2부터 Shard-Cluster 환경에서도 지원
- Multi-Document Transation 은 여러 작업, Collection, DB에 적용할 수 있으며 Transaction 이 Commit 되면 변경된 모든 데이터를 저장하고 Rollback 되면 모든 데이터 변경을 취소
- Commit 이 확정될 때까지 변경 중인 데이터는 누구도 참조할 수 없음( dirty_read : no)
- Embedded Document 및 Array 구조와 같은 단일-Dcoument Transaction 에 비해 성능지연 문제가 발생할 가능성이 있기 때문에, 이를 대체해서 사용하면 안됨
- FeatureCompatibility Version(fCV) 4.0 이후 환경에서 사용할 수 있으며 wiredTiger Storage Engine과 In-Memory Storage Engine에서 만 사용할수 있음(admin DB에서 설정 가능)db.adminCommand({ getParameter:1, featureCompatibilityVersion:1})
- db.adminCommand({ setFeatureCompatibilityVersion : "4.2"})
- Config, admin, local DB의 Collection 을 읽기/쓰기할 수 없으며 system.* Collection은 쓰기 작업을 수행할 수 없음. 또한 Multi Transaction 이 진행 중인 Collection은 인덱스를 추가 및 삭제할 수 없음
- Transaction 총 크기가 16 Mb 이상인 경우에도 필요한 만큼의 Oplog를 생성하여 처리할 수 있음
- Transaction 을 진행하기 전에 안전하게 write concern을 Majority 설정하는 것이 좋음
- Wired Tiger인 Primary Server와 In-Memory 인 Secondary Server환경에서도 Transaction 을 지원
반응형
'MongoDB > MongoDB-Study_완료' 카테고리의 다른 글
[MongoDB] [Study-12] Authentication & Role 정리 (0) | 2021.04.16 |
---|---|
[MongoDB] [Study11] 모델링 (0) | 2021.04.16 |
[MongoDB] [Study-9] Index (0) | 2021.04.16 |
[MongoDB][Study-8] Aggregation (0) | 2021.04.16 |
[MongoDB][Study-7] Find / FindAndModify / Cursor (0) | 2021.03.28 |