포도가게의 개발일지

MongoDB 본문

DB

MongoDB

grape.store 2022. 12. 20. 00:44
반응형

책 mongoDB 완벽 가이드

 

도큐먼트 지향 데이터 베이스. 관계형 모델을 사용하지 않는 주된 이유는 분산 확장을 쉽게 하기 위함이다. 도큐먼트 지향 데이터 베이스에서는 행 개념 대신에 보다 유연한 모델인 doc을 사용한다. 내장 doc와 배열을 허용함으로써 doc 지향 모델은 복잡환 계층 관계를 하나의 레코드로 표현할 수 있다. 이 방식은 최신 객체 지향언어를 사용하는 개발자의 관점에 매우 적합하다.

 

2. mongodb는 doc의 키와 값을 미리 정의 하지 않는다. 따라서 스키마리스하며 쉽게 필드를 추가하거나 제거할 수 있다. 이는 rdb의 경우 migration을 항상 고려해야하는 부분대비 큰 장점으로 와닿는다. mongo는 이로 인해 개발 과정을 빠르게 반복 할 수 있고. 모델을 실험해보기 쉽다. 수십개의 데이터 모델을 시도해본 후 최선의 모델을 선택할 수 있다. 

 

3. 데이터셋의 크기 증가는 데이터베이스를 어떻게 확장할것인가의 고민으로 이어진다. 스케일업과 아웃 갈림길에 서게 되며, 결국 스케일아웃이 관리는 어렵지만 비용 측면 과 확장이 용이한 스케일 아웃을 할 수 있다. 몽고디비는 분산 확장을 염두에 두고 설계되었으며, doc지향 데이터 모델은 데이터를 여러 서버에 더 쉽게 분산하게 해준다. doc 자동으로 재분배하고 사용자 요청을 올바른 장비에 라우팅만 하면 된다.

 

인덱싱

- 일반적인 보조 인덱스, 고유, compound, full-text 인덱싱 뿐만아니라, nested doc 및 배열과 같은 계층 구조의 보조 인덱스도 지원한다.

 

어그리게이션

- 데이터 처리 파이프라인 개념으로 서버 측에서 비교적 간단한 일련의 단계로 데이터를 처리함으로써 복잡한 분석 엔진을 구축하게 해준다.

 

특수 collection

- 로그, 최신 데이터 유지, 세션, 고정 크기 컬렉션과 같이 특정 시간에 만료해야 하는 데이터에 대해 ttl collection을 지원한다. 또한 criteria filter와 일치하는 doc에 한정된 부분 인덱스를 지원함으로써 효율성을 높이고 필요한 저장 공간을 줄인다.

 

복잡한 join은 mongodb에는 존재하지 않는다. lookup이 있지만 이 기능들은 분산 시스템에서 효율적으로 제공하기 어렵기 때문에, mongodb는 더 큰 확장성을 허용하기 위한 아키텍처 결정을 하였다.

 

고성능

- mongodb의 주요목표인 성능인 mongodb 설계에 지대한 영향을 미쳤다. mongo는 동시성과 처리량을 극대화하기 위해 wiredtiger에 기회적(opportunistic) locking을 사용 했다. 따라서 캐시처럼 제한된 용량의 램으로 쿼리에 알맞은 인덱스를 자동으로 선택할 수 있다.

 

같은 유형의 doc을 하나의 collection에 넣음으로써 효율적으로 인덱싱할수 있다. 스키마를 만들고 관련된 유형의 doc을 그룹화하는데는 타당한 이유가 있다. 애플리케이션 스키마는 기본적으로 필요하지는 않지만 정의하면 좋다. mongo는 doc 유효성검사기능과 객체-doc 매핑 라이브러리를 이용한다. 

 

서브컬렉션을 네임스페이스에 .를 붙여 체계화 한다. 하지만 이는 project.timeline, project.block은 단지 체계화를 위함이며 project컬렉션이 없어도 된다. 서브컬렉션은 mongodb 데이터를 체계화 하는 훌륭한 방법이다.

 

mongo 단일 인스턴스는 여러 데이터베이스를 호스팅 할 수 있으며, 각 데이터베이스를 완전히 독립적으로 취급할 수 있다. 예약된 데이터베이스 이름

admin : 인증과 권한부여 역할.

local : 단일 서버에 대한 데이터를 저장한다.

config: 샤딩된 mongodb 클러스터는 config database를 사용해 각 샤드의 정보를 저장한다.

 

mongod는 args 없이 실행하면 기본 데이터 디렉토리로 /data/db를 사용한다.mongodb는 기본적으로 27017 포트 소켓연결을 기다린다.

 

 

ObjectId

 - 는 여러 장비에 걸쳐 전역적으로 고유하게 생성하기 쉽게 설계됐다. 자동 증가하는 기본 키처럼 oid를 사용하는 주요 이유는 mongodb 분산 특성 때문이다. 여러 서버에 걸쳐 자동으로 증가하는 기본 키를 동기화하는 작업은 어렵고 시간이 걸린다. mongo는 샤딩된 환경에서 고유 식별자를 생성하는 것이 매우 중요했다. oid는 타임스탬프 + 랜덤 + 카운터로 구성 된다. 0 1 2 3 4 5 6 7 8 9 10 11 첫 4바이트는 타임스탬프, 타임스탬프가 맨 처음에 온다는 것은 oid가 대략 입력 순서대로 정렬된다는 의미다. 이는 확실히 보장되지는 않지만 oid를 효율적으로 인덱싱하는 다소 멋진 특성이 있다.

 

mongorc

- 자주 로드되는 스크립트를 셀이 시작할때 마다 실행되는 .mongorc.js 파일에 넣을 수 있다.

 

삽입 유효성 검사

- mongodb는 삽입된 데이터에 _id가 존재하지 않으면 추가하고, 크기검사 진행,\

 

insert, remove 레거시니까 쓰지말자

 

갱신은

- 원자적으로 이루어지기 때문에 먼저 도착한 요청 부터 처리된다. 기본동작을 원하지 않으면 도큐먼트 버저닝 패턴을 고려하자.

 

$addToSet을 이용하면 중복을 피할 수 있다.

 

null은 스스로 일치하는 것을 찾는다. 하지만 존재하지 않음과도 일치하기 때문에 해당 필드가 없는 도큐먼트를 찾을 수 있다. 값이 null인 키만 찾고 싶으면 키가 null값을 쿼리하고 $exist 조건절을 사용해 null 존재 여부를 확인하면 된다.

 

skip 도큐먼트 수가 많을 때 모든 생략된 결과물을 폐기해야 하므로 결과가 많으면 느려진다. 대부분의 데이터베이스에서는 skip을 위해 인덱스 안에 메타데이터를 저장하지만 몽고디비는 아직이다.

 

@Index

복합 인덱스

각 인덱스 항목은 나이와 사용자명을 포함하고 레코드 식별자를 가리킨다.

레코드 식별자는 내부에서 스토리지 엔진에 의해 사용되며 도큐먼트 데이터를 찾는다.

몽고디비에서 쿼리에 종류에 따라 인덱스를 사용하는 방법이 다르다. 가장 많이 사용하는 3가지 방법으로

동등 쿼리

범위 쿼리

다중값 쿼리

정렬이 포함된 다중 값 쿼리

인덱스는 사용자명을 정렬된 순서로 반환하지 않으며, 쿼리는 사용자명에 따라 정렬된 결과를 요청한다. 이는 몽고디비가 이미 정렬되어있는 인덱스를 통과하지 않고, 결과를 반환하기 전에 메모리에 정렬을 해야함을 의미한다. 따라 일반적인 쿼리보다 비효율 적이다. 속도는 검색 조건과 일치하는 결과의 양에 의해 결정되며, 결과가 32메가바이트 이상이면 몽고디비는 정렬 거부 에러를 던지게 된다.

-> 오류를 줄이기 위해 정렬작업을 지원하는 인덱스를 생성해야 하며, limit을 이용하여 32MB이하로 줄이자.

 

몽고디비엔진이 인덱스를 선택하는 방법

쿼리가 들어오고 인덱스 5개중 3개가 쿼리 후보로 식별됬다고 가정해보자. 몽고는 3개의 쿼리 플랜을 만들고, 각각 다른 인덱스를 사용하는 3개의 병렬스레드에서 쿼리를 실행한다. 어떤 스레드에서 가장 빨ㄹ ㅣ결과를 반환하는지 확인하기 위함이다.

 

이 과정에서 이긴 쿼리 플랜이 승자가 되며, 앞으로 동일한 모양을 가진 쿼리에 사용할 인덱스로 선택된다는 점이 중요하다. 인메모리 정렬을 하면 비용이 많이 들기 때문에 정렬 순서는 중요한 부분이다. 시간이 지나 컬렉션과 인덱스가 변경되면 쿼리 플랜이 캐시에서 제거되고, 몽고는 다시 가능한 쿼리 플랜을 실험해 해당 컬렉션 및 인덱스 집합에 가장 적합한 플랜을 찾는다. 쿼리 플랜 캐시는 명시적으로 지울 수 있으며, mongod 프로세스를 다시 시작할 때도 삭제된다.

 

복합 인덱스 사용

- 읽기와 쓰기를 가능한 효율적으로 수행하도록 인덱스를 설계하자.

- 인덱스의 selectivity을 고려한다.우리는 특정 쿼리 패턴에서 스캔할 레코드 개수를 인덱스가 얼마나 최소화하는지에 관심 있다.

- executionStatus 필드는 선정된 쿼리 플랜에 대해 완료된 퀄 ㅣ실행을 설명하는 통계를 포함한다.

- explain 출력은 쿼리 플랜을 단계 트리로 표시한다.

- 일반적으로 동등 필터를 사용할 필드가 다중값 필터를 사용할 필드보다 앞에 오도록 복합 인덱스를 설계해야 한다.

- 인덱스가 어떻게 만들어지는지 생각해보면 왜 그런지 알 수 있다. [class_id, student_id] 인덱스는 다음과 같은 키쌍으로 구성된다.

class_id, student_id

복합 인덱스를 설계할 때는 인덱스를 사용할 공통 쿼리 패턴의 동등 필터, 다중값 필터, 정렬 구성요소를 처리하는 방법을 알아야 한다.

- 복합 인덱스에서 자주 발생하는 트레이드 오프로, 인메모리 정렬을 피하려면 반환하는 도큐먼트 개수보다 더 많은 키를 검사해야 한다. 인덱스를 사용해 정렬하러면 몽고가 인덱스 키를 순서대로 살펴 볼 수 있어야 한다. 즉 복합 인덱스 키 사이에 정렬 필드를 포함해야 한다. 그로 인해 새로운 복합 인덱스의 키는 [class_id, final_grade_ student_id]와 같이 정렬되어야 한다. 정렬 구성 요소는 동등 필터 바로뒤, 다중값 필터 앞에 포함된다. 이 인덱스는 쿼리에서 고려하는 키 집합을 매우 선택적으로 좁힌다. 해당 복합 인덱스는 몽고가 결과셋에 포함될 도큐먼드보다 더 많은 도큐먼트의 키를 검사하게 한다. 하지만 인덱스를 사용해 도큐먼트를 정렬함으로써 실행 시간을 절약할 수 있다.

키 방향 선택하기

 - 두개 이상의 검색 조건으로 정렬할 때는 인덱스 키의 방향이 서로 달라야 한다. 예를 들어 나이가 적은 사용자부터 많은 사용자 순으로, 사용자명은 z~a로 컬렉셔늘 정렬한다고 가정하자. 복합 정렬을 서로 다른 방향으로 최적화 하려면 방향이 맞는 인덱스를 사용해야 한다. 이 예제에서는 {age:1, username:-1}을 사용해 다음과 같이 데이터를 구성한다. 역방향 인덱스는 서로 동등함을 알아두자. 인덱스 방향은 다중 조건에 따라 정렬할 때만 문제가 된다. 단일 키로 정렬하면 몽고 디비는 인덱스를 쉽게 역순으로 읽을 수 있다. 방향은 다중키로 정렬할 때만 문제가 된다....

 

- 커버드 쿼리

 - 인덱스는 항상 적합한 도큐먼트를 찾는데 사용되고, 실제 도큐먼트를 가져오기 위해 곧바로 포인터를 따라간다. 하지만 쿼리가 단지 인덱스에 포함된 필드를 찾는 중이라면 도큐먼트를 가져올 필요가 없다. 만약 인덱스가 쿼리가 요구하는 값을 모두 포함하면, 쿼리가 커버드 된다고한다. 실무에서는 도큐먼트로 되돌아가지말고 항상 커버드 쿼리를 사용하자. 이방법으로 작업 셋을 훨씬 자게 만들 수 있다.

- 쿼리가 확실히 인덱스만 사용하게 하려면(필드가 인덱스의 일부가 아닌경우) _id필드를 반환받지 않도록 반환받을 키를 지정해야 한다.

- 커버드 쿼리에 explain을 실행하면 결과에 FETCH 단계의 하위단계가 아닌 IXSCAN 단계가 있으며, executionStatus에서는 totalDocsExamined의 값이 0이된다.

 

암시적 인덱스

 - {a:1, b:1, c:1 , ... z:1}의 인덱스를 설정하였다면 이는 {a:1}, {a:1, b:1}, {a:1, b:1, c:1} 등으로 인덱스를 가진다.이로 인해 {b:1}를 이용하는 쿼리는 최적화 되지 않는다.

 

$연산자 인덱스 사용법

일반적으로 부정조건 $ne는 비효율적이다. 인덱스를 활용하기는 하지만 지정된 항목을 제외한 모든 인덱스 항목을 살펴봐야 하므로 기본적으로 전체 인덱스를 살펴봐야 한다. $nin은 항상 테이블 스캔을 사용한다. 이런 종류의 쿼리를 신속하게 실행해야 한다며 몽고디비가 인덱스를 사용하지 않는 일치를 시도하기 전에, 결과 셋이 적은 수의 도큐먼트를 반환하게 끔 필터링하는 인덱스를 사용하도록 쿼리에 추가하자.

 

범위

 - 다중 필드로 인덱스를 설계할 때 완전 일치가 사용될 필드는 먼저 범위가 사용될 필드는 마지막에 놓자. 이는 쿼리가 첫 번째 인덱스 키와 정확히 일치하는 값을 찾은 후 두 번째 인덱스 범위 안에서 검색하게 해준다. 하나의 쿼리에 두 개의 범위를 사용하면 비효율적인 쿼리 플랜이 된다.

 

or 쿼리

 - 현재 몽고디비는 쿼리당 하나의 인덱스만 사용할 수 있다. 유일한 예외는 $or이다 두개의 쿼리를 수행하고 결과를 합치므로 절마다 하나씩 인덱스를 사용할수있다.,(ex. {x:123, y:456}. 하지만 일반적으로 두 번 쿼리해서 결과를 병합하면 한 번 쿼리할 때 보다 훨씬 비효율적이므로 $or보다는 $in을 사용하자.

 

내장 도큐먼트 인덱싱하기

 - 인덱스는 일반적인 키에 생설될 때와 동일한 방식으로 내장 도큐먼트 키에 생성될 수 있다. nested된 subdoc에 예를들어 metadata.displayName을 인덱스를 설정 할 수 있다.

 

배열 인덱싱하기

- 배열에도 인덱싱을 통해 빠르게 찾을 수 있다. comments라는 field에 date를 인덱싱하여 가장 최근에 댓글이 달린 블로그 게시물을 찾을 수 있다. 하지만 입력, 갱신, 제거 작업을 하려면 모든 배열 요소가 갱신돼야 하므로 배열 인덱스를 단일값 인덱스보다 부담스럽게 다가온다.

 

인덱스 카디널리티

 - 카디널리티는 컬렉션의 한 필드에 대해 고유값이 얼마나 많은지를 나타낸다. gender는 예를들어 가질 수 있는 값이 두가지뿐이며, 이는 매우 낮은 카디널리티로 간주된다. name이나 eamil의 경우 각 doc마다 유일값이기 때문에 높은 카디널리티를 가지게 된다. 일반적으로 카디널리티가 높을수록 인덱싱에 도움이된다. 인덱스가 검색범위를 훨씬 작은 결과 셋으로 빠르게 좁힐수있기 때문이다. 이로 인해 적어도 복합 인덱스에서 높은 카디널리티를 낮은 카디널리티보다 앞에 두자. 

 

인덱스 관리

- 인덱스는 컬렉션당 한 번만 만들어야 한다. 데이터베이스의 인덱스 정보는 모두 system.indexes 컬렉션에 저장되며 이것은 예약된 컬렉션이다.

 

어그리게이션

 - 파이프라인을 구축할 때 한 단계에서 다른 단계로 전달해야 하는 도큐먼트 수를 반드시 제한하자.

 

스키마설계

 - nested : 작은 서브doc, 주기적으로 변하지 않는 데이터, 증가량이 적은 doc, 두번째 쿼리를 수행하는데 자주 필요한 데이터, 빠른 읽기, 일반적으로 빠른 읽기에 적합

 

 - ref : 큰 sub doc, 자주 변하는 데이터, 즉각적인 일관성, 증가량이 많은 doc, 결과에서 자주 제외되는 데이터, 일반적으로 빠른쓰기에 적합

 

연속 document

 - 

 

데이터 조작을 위한 최적화

 - 읽기 최적화는 일반적으로 올바른 인덱스를 사용해 하나의 도큐먼트에서 가능한 한 많은 정보를 반환하는 것과 관련있다.

- 쓰기 최적화는 보통 갖고 있는 인덱스 개수를 최소화하고 갱신을 가능한 한 효육적으로 수행하는것과 관련이 있다.

 

-> 빠른 쓰기에 최적화된 스키마와 빠른 읽기에 최적화된 스키마 사이에는 종종 트레이드오프가 존재하므로 어느것이 어플리케이션에 더 중요한지 결정해야 한다. 

 

데이터베이스와 컬렉션 구상

 - 모양이 꽤 다른 도큐먼트를 집계할려면 모두 같은 컬렉션에 있어야 한다.( 다른 컬렉션 혹은 데이터베이스에 있으면 $merge 단계를 사용할 수 있다.)

- 컬렉션에서는 락과 저장을 중요하게 고려해야 한다.

 

일관성 관리

 - 지금까지 큐는 하나의 연결에 대한 큐였다. 셸을 두개 열면 데이터베이스에 대한 연결이 두개가 된다. 하나의셸에서 삽입을 수행하면 이후에 다른 셸에서는 발생하는 쿼리는 삽입된 도큐먼트를 반환하지 못한다. 이러한 동작은 특히 루비 파이썬 자바 드라이버를 사용할 때 염두에 둘만한데, 세 언어 모두 커넥션 풀링을 사용하기 때문이다. 드라이버는 효율성을 위해 서버에 대한 여러 연결풀을 열고 요청을 분산한다. 하지만 세 드라이버 모두 일련의 요청이 하나의 연결에 의해 처리되도록 보족하는 메카니즘을 가진다.

 - 몽고디비는 읽을 데이터의 일관성과 격리 속성을 제어하는 readConcern 옵션을 제공한다. writeConcern과 결합하면 애플리케이션에 대한 일관성과 가용성 보장을 제어할 수 있다. local availabe majority linearizable snapsho이라는 5개의 수준이 있다. majority는 대부분의 복제 셋 멤버에서 확인된 내구성 있는 데이터만 반환하며 롤백되지 않는다. 몽고디비는 linearizable readConcern으로 결과를 반환하기 전에 동시 실행되는 쓰기가 완료될 때까지 기다린다.

 

스키마 마이그레이션

 - 마지막으로 스키마가 변경될 때 모든 데이터를 마이그레이션하는 방법이 있다. 하지만 일반적으로 이방법은 바람직하지 않다. 몽고디비는 시스템에 많은 부하를 주는 마이그레이션을 피하기 위해 동적 스키마를 갖도록 허용한다. 하지만 몽고디비는 이러한 마이그레이션을 지원하는 트랜잭션을 지원한다.

 

- 되도록이면 도큐먼트 버전관리 패턴을 적용할 수 있는지 고려해야한다.

 

몽고 디비를 사용하지 않는 경우

 - 몽고디비가 대부분의 애플리케이션에 잘 작동하는 범용 데이터베이슺만 다음과 같은사례에는 부적합하디

 + 다양한 유형의 데이터를 여러 차원에 걸쳐 조인하는 작업은 관계형 db에 적합하다.

 

 

복제

 - 복제는 데이터의 동일한 복사본을 여러 서버상에서 보관하는 방법이며 실제 서비스를 배포할 때 권장된다. 한 대 또는 그이상으 ㅣ서버에 이상이 발생하더라도, 복제는 애플리케이션이 정상적으로 동작하게 하고 데이터를 안전하게 보관한다.

 

 - 레플리카 셋을 생성함으로써 복제를 설정할 수 있다. 프라이머리 서버 한대와, 복사본을 갖는 세컨더리 서버 여러 대로 이루어진다. 프라이머리 서버에 장애가 발생하면 세컨더리 서번느 자신들 중에 새로운 프라이머리 서버를 선출할 수 있다.

- 이 설정 유형을 통해 mongodb가 고가용성 및 DR을 처리하는 방법을 이해할 수 있다.

 

majority

 - 언제나 프라이머리를 하나만 갖도록 셋을 구성하는 것이 중요하다. 다섯 멤버로 구성된 복제 셋을 예로 123이 하나의 데이터센터에 있고 나머지 45가 다른 데이터센터에있다면 복제 셋의 과반수는 언제나 첫번째 데이터센터일 가능성이 높다. 

그 외 케이스로 각 데이터 센터 내 서버개수가 동일하고 , 또 다른 위치에 동점 상황을 판가름할 서버가 있는 경우 두 데이터 센터의 선호도가 동일할 때 적합한 설계다.

 

선출 방식

 - 복제 셋 멤버는 2초마다 서로 하트비트를 보내며 10초 이내에 멤버가 하트비트가 반환되지 않므면, 다른 멤버가 그 불량 멤버를 접근할 수 없음으로 표기한다. 복제 셋 멤버들은 우선순위가 가장 높은 멤버가 프라이머리가 될 때까지 계속해서 선출을 호출한다.

 

- 멤버가 2명인 서버에 대해 몽고는 프라이머리 선출에 참여하는 용도로만 쓰이는 아비터 라는 특수한 멤버를 지원한다. 아비터는 데이터를 가지지 않으며 클라이언트에 의해 사용되지 않는다. 오로지 선출을 목적으로만 존재한다. 하지만 일반적으로 아비터가 없는 배포가 바람직하다. 그리고 이왕이면 다른 멤버와 분리된 섹터에서 아비터를 실행함으로써 아비터가 복젲 셋에 외부 관점을 갖게 하면 좋다.

 

 

동기화

- 몽고는 프라이머리가 수행한 쓰기를 모두 포함하는 로그 oplog를 보관함으로써 복제를 수행한다. oplog는 프라이머리의 로컬 데이터베이스에 있는 제한 컬렉션이며, 세컨더리는 이 컬렉션에 복제를 위한 연산을 쿼리한다. 각 세컨더리는 프라이머리로부터 복제한 작업을 각각 기록하는 oplog를 보관한다. 세컨더리는 동기화하는 멤버로부터 연산을 가져와 데이터셋에 적용한뒤 자신의 oplog에 쓴다.

 

세컨더리가 어떤 이유로든 다운되면, 재시작할 때 oplog에 있는 마지막 연산과 동기화한다. oplog는 크기가 고정되어 있으므로 담을 수 있는 연산의 수가 정해져 있다. 일반적으로 oplog는 쓰기 연산이 시스템에 적용될 때와 비슷하게 공간을 차지한3다. 연산 하나가 프라이머리에 수행되면, 영향받는 도큐먼트 개수당 하나씩 oplog 연산으로 분해된다.

 

초기 동기화를 수행하면 해당 멤버는 자주 사용되는 데이터를 추출해 메모리로 페이징하며, 이는 멤버가 급격하게 느려기제 한다. 실제로 멤버가 동기화 소스의 oplog보다 뒤처지면 초기 동기화를 진행할 수 없다. 그리고 동기화 소스에서 oplog를 복사한 후 이러한 작업을 비동기로 처리하게된다. 투표수가 1인 멤버는 투표수가 0인 멤버와 동기화 할 수 없다.

 

 

하트비트

- 멤버는 다른 멤버의 상태 정보를 알아야 한다. 멤버는 복제 셋의 대한 최신 정보를 유지하기 위해 모든 멤버로 2초마다 하트비트 요청을 보낸다. 하트비트의 가장 중요한 기능은 복제 셋의 과반수 도달 가능 여부를 프라이머리에 알리는 기능이다. 프라이머리가 더는 서버의 과반수에 도달할 수 없다면, 스스로를 강등해 세컨더리가 된다.

 

롤백

- 롤백은 복구 전에 복제되지 않은 연산을 원래 상태로 되돌리는데 사용된다. 차이로 인해 p.338 oplog 126이 있는 서버는 자신이 가진 연산을 살펴보고 그 연산으로부터 영향 받은 각 doc 버전을 rollback dir에 있는 .bson 파일에 쓴다.

 

클라이언트-복제셋 연결 동작

- 몽고db 클라이언트 라이브러리('드라이버')는 서버가 독립 실행형 몽고db 인스턴스든 복제 셋이든 관계없이 몽고db 서버와의 통신을 관리하도록 설계 됐다. 복제셋이면 기본적으로 드라이버는 프라이머리에 연결되고 모든 트래픽을 프라이머리에 라우팅한다. 애플리케이션은 복젲 셋이 종요히 백그라운드에서 대기 상태를 유지하는 동안 마치 독립 실행형 서버(no rep set)와 통신하듯이 읽기와 쓰기를 수행할 수 있다.

 

- 복제 셋의 목적은 네트워크 파티션이나 서버가 다운될 때도 데이터의 가용성을 높이는 것이다. 프라이머리가 다운되면 드라이버는 새로운 프라이머리를 찾고 가능한 한 빨리 프라이머리로 요청을 라우팅한다. 그러나 도달할 수 있는 프라이머리가 없는 동안에는 애플리케이션이 쓰기를 수행할 수 없다.(읽기 쓰기 포함). 하지만 애플리케이션에서 필요하다면 읽기 요청에 세컨더리를 사용하도록 드라이버를 구성 할 수 있다.

 

- w(write concern값은 프라이머리를 포함한다. 1이게되면 프라이머리에만 쓰고 나머지는 기다리지 않겠다는 것이다,.)

 

세컨더리 읽기를 하는 이유

- 애플리케이션에 지연율이 낮은 읽기와 쓰기가필요하면 반드시 샤딩을 이용해야한다. 복제 셋은 오직 하나의 위치에만 쓰기를 허용한다. 프라이머리에 두고 싶지 않은 오프라인 처리를 위해 꽤 많은 인덱스가 필요할 수 있다. 이때 세컨더리를 프라이머리와 다른 인덱스로 설정할 수 있따. 세컨더리를 이러한 용도로 사용하라면, 복젲 셋에 연결대신 드라이버에서 세컨더리로 직접적인 연결을 만든다.

 

관리

 - 복제 셋 구성은 항상 local.system.replset 컬렉션의 doc에 보관된다. 항상 rs.initiate(config)를 수생할 때 config를 전달해야 한다. 그렇지 않으면 mongodb 는 자동으로 단일 멤버 복제 셋을 위한 config를 생성한다. 이 config는 사용자가 원하는 호스트명을 사용하지 않으며, 복제 셋을 올바르게 구성하지 않는다. 복제 셋 멤버는 50개, 투표 멤버는 7개로 제한된다. 이는 네트워크 트래픽량을 줄이고 선출에 걸리는 시간을 제한하기 위함이다.

 

- 세컨더리는 일반적으로 같은 데이터 센터에 있는 멤버를 동기화하므로 복제 사슬이 길어질 수 있다.

 

- 프라이머리의 oplog는 유지보수시간으로 여겨진다. 프라이머리의 oplog 길이가 한시간정도라면, 잘못된 부분을 고칠 수 있는 시간이 한시간정도라는 의미다. 따라서 뭔가가 잘못될 때 약간의 틈이 있으려면 며칠에서 1주 정도 데이터를 보유할 수 있는 oplog가 바람직하다.

 

인덱스 설정

- 만약 모든 세컨더리가 동시에 인덱스를 구축하기 시작한다면 복제 셋의 거의 모든 멤버는 인덱스 구축이 완료될 때까지 오프라인 상태가 된다. 이 과정은 복제 셋 에만 해당된다. 특히 unique 인덱스를 만들 때는 컬렉션에 대한 모든 쓰기를 중지해야한다. 중지 하지 않으면 복제 셋 멤버끼리 데이터가 일치하지 않게 될 수 있다.

 애플리케이션에 대한 영향을 최소화 하려면 인덱스는 한번에 한 멤버씩 구축하는 것이 바람직하다. 

1. 세컨더리를 종료한다.

2. 종료한 센커더리를 독립 실행형 서버로 재시작한다.

3. 재시작한 서버에 인덱스를 구축한다.

4. 인덱스 구축이 완료되면 서버를 복제 셋 멤버로 재시작한다. 멤버를 재시작할 때 명령행 옵션이나 구성파일에 disablelogical~매개변수가 있으면 제거해야한다.

5. 복제 셋의 각 세컨더리에 1단계부터 4단계 까지 반복한다.

 

이후 프라이머리를 인덱스를 구축해야하는데 2가지 선택지가있다. p.370

- 인덱스가 다른 멤버는 절대 프라이머리가 될 수 없으므로 우선선위가 0이다. unique index를 구축할때는 프라이머리가 중복 삽입을 하지 않아야 하며, 인덱스를  프라이머리에 먼저 구축해야한다. 그렇지않으면 프라이머리가 중복삽입후에 복제과정에서 세컨더리가 죽게된다.

 

샤딩

- 샤딩은 여러 장비에 걸쳐 데이터를 분할하는 과정을 일컬으며, 때때로 파티셔닝이라는 용어로도 불린다. 각 장비에 데이터의 서브셋을 넣음으로써 더크 크거나 강력한 장비 없이도 더 많은 수의 덜 강력한 장비로 더많은 데이터를 저장하고 더 많은 부하를 처리할 수 있다. 샤딩은 이외 용도로도 , 더 자주 접근하는 데이터를 성능이 좋은 하드웨어에 배치하거나, 지역에 따라 데이터셋을 분할해 주로 접근하는 애플리케이션 서버와 가까운 컬렉션에서 doc의 서브셋을 찾을 수 있다. 예를 들어 사용자가 특정 로케일을 기반으로 할때

 

- 수동샤딩은 어떤 데이터베이스 소프트웨어를 사용하든 대부분 수행할 수 있다. 수동 샤딩을 사용하면 애플리케이션이 여러 db와 연결을 유지하며 각 서버는 완전히 독립적이다. 애플리케이션은 각기 다른 데이터를 다른 서버에 저장하고 데이터를 가져오기 위해 적절한 서버에 쿼리하는 과정을 관리한다. 다만 샤드를 추가하거나 삭제할 때 혹은 데이터 분산이나 부하 패턴이 변화할 때는 유지하기 어렵다.

 

 - 하지만 몽고db는 애플리케이션에서 구조를 추상화하고 시스템을 관리하게 편하게, 자동 샤딩을 지원한다. 운영 측면에서 mongodb가 샤드에 데이터 분산을 자동화 하므로 용량을 추가하고 제거하기 쉽다. 개발 및 운영 측면에서 샤딩은 몽고db를 구성하는 가장 어렵고 복잡한 방법이다. 모니터링해야할것도 많고, 클러스터에서 데이터가 자동으로 옮겨 다니기 때문이다. 따라서 독립실행형이나 복제셋에 먼저 익숙해져야한다. 또한 복제 셋과 마찬가지로 샤드클러스터를 구성하고 배포할 때도 몽고db 옵스 매니저나 mongodb 아틀라스를 사용하기를 장한다. 

 

- 몽고 db 샤딩을 통해, 많은 장비(샤드)의 클러스터를 생성하고, 각 샤드에 데이터 서브셋을 넣음으로써 컬렉션을 쪼갤 수 있다. 

복제는 : 여러 서베어 데이터의 복사본을 생성하므로 모든 서버는 다른 서버의 미러 이미지

샤드는 : 각각 다른 데이터 서브셋을 포함한다.

 

 - 샤딩의 한가지 목적은 여러개의 샤드 클러스터가 하나의 장비처럼 보이게 하는 것이다. 이러한 세부사항을 애플리케이션으로 부터 숨기기 위해 샤드 앞단에 mongos라는 라우팅 프로세스를 실행한다. mongos는 어떤 샤드가 어떤 데이터를 포함하는지 알려주는 컨텐츠 목차가 있다.

mongos shard
mongod replset

- 각 샤드에 대해 하나의 독립현 Mongod를 실행할 수 있지만 각 샤드를 복제 셋으로 생성하면 샤드 클러스터의 전형적인 아키텍처를 보다 명확하게 볼 수 있다.

- 샤딩은 주로 하드웨어 및 비용 제약을 해결하거나 애플리케이션에 나은 선능을 제공하려고 데이터 셋을 분할하는 데 사용된다. 예를들면 지리적 분할

 - shard 2 node 3을 주게되면 최종적으로 생성되는 프로세는 10개이다 각 샤드마다 3개의 replset이 생성되며 config server각 node 3개로 구성된 복제셋 한개를 띄우게되며 마지막으로 mongos 1개가 있다.

-일반적으로 mongos의 포트는 20009에서 실행된다. 모든 데이터베이스마다 무작위로 선정된 프라이머리 샤드에 있다. 

-

- 컬렉션을 샤딩할 때 샤드 키를 선택하는데 이는 몽고디비가 데이터를 분할하는데 사용하는 필드다. 샤드 키를 선택하는 것은 컬렉션 내 데이터 순서를 선택하는 것으모 생각할 수 있다. 인덱싱과 유사한 개념이며 컬렉션이 커질수록 샤드키가 컬렉션에서 가장 중요한 인덱스가 된다. 샤드 키를 만들려면 필드에 인덱스를 생성해야한다. 그후 생성 된 인덱스를 이용하며 샤딩을 할 수 있다.

 

 - 일반적으로 샤딩을하기전 컬렉션은 단일 청크로 저장된다. 하지만 샤딩 이후에 샤드 키를 기반으로 컬렉션을 더작은 청크로 분할한다. 이후에 여러 샤드에 분산되게 된다. 일반적으로 쿼리에서 샤드 키를 사용하지 않으면 mongos는 모든 샤드에 쿼리를 보내햐 한다. 샤드 키를 포함하며 단일 샤드나 샤드 서브셋으로 보낼 수 있는 쿼리를 타겟 쿼리하 한다. 모든 샤드로 보내야 하는 쿼리는 분산 수집 쿼리라고 한다.

 

언제 샤드?

 - 너무 일찍 샤딩을 하면 좋지 않은데, 배포 운영이 더 복잡해지며 나중에 변경이 어려운 구조에 대한결정을 내려야 하기 때문이다. 반면 너무 늦어도 과부화된 시스템을 중단없이 샤딩하기 어렵기 때문이다. (샤용가능한 메모리를 늘릴때, 사용 가능한 디스크 공간을 늘릴때,  서버흐 ㅣ부하를 줄일 때, 한개의 Mongod가 다룰 수 있는 처리량보다 더 많이 데이터를 읽거나 쓸 때)

 

 - 고가용성을 위해 mongos는 최소 두개가 필요하다 하지만 너무 많은 수는 리소스 경합을 유발하기 때문에 적은 수의 라우터를 사용하기를 권장한다.

- 샤드를 추가했으면 반드시 mongos를 통해서만 요청을보내도록 하고 mongod 접속은 방화벽 설정을 하자

- mongodb는 데이터를 어떻게 분산할 지알려주기 전에는 자동으로 데이터를 분산하지 않는다. 분산하려는 데이터베이스와 컬렉션을 명시적으로 알려줘야한다.

 - 데이터베이스는 항상 데이터베이스 내 컬렉션 보다 먼저 샤딩해야 한다.

- 기존 컬렉션을 샤딩하려면 해당 필드에 인덱스가 있어야 한다.

- 컬렉션이 크면 최초 분산을 끝내는 데 몇시간이 걸릴 수 있다.

- 샤드 doc을 청크로 나누는 이유는 항상 하나의 청크는 하나의 샤드에 위치하게 만들기 위해

 

밸런서

 - 밸런서는 데이터 이동을 책임진다. 현재 3.4버전 이후에 config server 복제셋 프라이머리 멤버에 존재한다.

 - 데이터가 이동이 완료될 때 까지 모든 읽기와 쓰기는 이전 청크로 전달된다. 데이터 이전이 완료된후 이전 데이터 접근 시도는 전부 프로세스 오류가 되지만. mongos가 implicit하게 처리해준다.

 

샤드키는

- 배열이 될 수 없다.

- 인덱스와 마찬가지로 카디널리티가 높은 필드에 좀 더 효율적이다.

- 또한 키조합의 카디널리티가 높은 것도 좋다.

 

인증은 신원확인

권한은 접근제어

 

저널링을 통한 멤버 수준의 영속성

 - 서버 오류 발생시 저널이라는 로그 선행기입wal을 사용한다. 이것을 사용하면 디비 자체에변경 사항을 적용하기 전에 디스크에 변경사항을 간단히 작성한다. 많은 데이터베이스들이 이러한 시스템을 사용한다.

 

하지만 몽고4.0부터 애플리케이션이 복제 셋에 쓰기를 수행하면, 모든 복제된 컬렉션에 데이터에 대허 몽고디비는 저널 항목을 생성한다. 몽고는 oplog를 기반으로 하는 명령문 기반 복제를 사용한다. 기본적으로 저널 항목을 50ms마다 디스크로 플러시하고 데이터베이스 파일은 60초마다 디스크로 플러시한다. 데이터 파일을 플러시하는 60초 간격을 체크포인트!라고 한다. 저널은 마지막 체크포인트 이후 기록된 데이터의 영속성을 제공한다. 때문에 서버가 갑자기 죽더라도 재시작할 때 저널을 사용함으로써 종료 전에 디스크에 플러시 되지않은 쓰기를 다시 진행할 수 있다.

 

저널 파일의 경우 몽고디비는 dbPath dir 아래에 journal이라는 서브디렉토리를 만든다. 저널 파일의 크기 제한은 약 100mb이다. 저널 파일이 제한을 초과하면 db는 새 저널파일을 만들고 거기에 새 기록을 쓰기 시작한다. 저널파일은 체크포인트 이후의 데이터만 필요하므로 db는 새 체크포인트가 작성될때 오랜된 저널들을 삭제한다.

'DB' 카테고리의 다른 글

[DB] MongoDB[2]  (0) 2022.04.10
[DB] MongoDB[1]  (0) 2022.04.03
DB Index?(업데이트 필요)  (0) 2022.02.07
SQL vs. NoSQL  (0) 2022.02.07
SQL query 구문 연습  (0) 2021.12.23
Comments