-
[MongoDB] 몽고디비 정리Software Development/Database 2021. 7. 7. 19:27
'몽고디비 인 액션 '의 내용을 바탕으로 정리하였습니다.
코어 서버
MongoDB를 상용에서 구동할 때는 복제가 권장사항이며, 두 개의 복제 노드와 아비터 모드로 구동하는 mongod로 이루어진 복제 세트가 일반적이다.
자바스크립트 셸
MongoDB 명령어 셸은 자바스크립트에 기반한 툴인데, 데이터베이스를 관리하고 데이터를 조작하는 데 사용.
데이터베이스 드라이버
모든 드라이버는 애플리케이션상에서 MongoDB 서버와 통신하기 위해 사용된다. 모든 드라이버는 질의, 결과 검색, 데이터 쓰기, 데이터베이스 명령어를 실행하기 위한 기능을 가지고 있다.
커맨드라인 툴
MongoDB는 여러 가지 커맨드 라인 유틸리티가 번들로 제공된다.
- mongodump, mongorestore: 데이터베이스 백업과 복구를 위한 표준 유틸리티다. mongodump는 BSON 형태로 데이터를 저장하므로 백업만 할 때 적합하다. Mongodump는 핫 백업에 사용할 수 있다는 장점이 있고, mongorestore로 쉽게 복구될 수 있다.
- mongoexport, mongoimport: JSON, CSV, TSV 타입의 데이터를 export, impor할 수 있다. mongoimport는 대용량 데이터를 초기에 임포트하기 좋다.
- mongosniff: 데이터베이스에 요청된 오퍼레이션을 보기 위한 와이어 스나이핑 툴. BSON을 읽기 쉬운 셸 문장으로 변환해 준다.
- mongostat: 서버에 대한 유용한 통계 정보 제공(포당 수행되는 삽입, 질의, 수정, 삭제 가상 메모리의 양, 서버에 대한 연결의 수)
- mongotop: top과 비슷한데, MongoDB와 시스템을 폴링해서 각 컬력센의 데이터를 읽고 쓰는 데 걸린 시간의 양을 보여줌.
- mongoperf: MongoDB 인스턴스가 구동되고 있을 때 발생하는 디스크 오퍼레이션을 이해하는 데 도움을 준다.
- mongooplog: MongoDB oplog에서 어떤 일이 발생하고 있는지를 보여 준다.
- bsondump: BSON -> JSON처럼 사람이 읽을 수 있느 형태로 제공
MongoDB를 사용하는 이유
관계 데이터베이스와 키-값 저장 시스템의 장점만을 모아서 설계.
키-값 저장 시스템은 단순성으로 인해 속도가 매우 빠르고 확장도 용이.
관계 데이터베이스는 수평적인 확장은 어려우나 데이터 모델과 강력한 쿼리 언어를 제공
MongoDB 드라이버 작동 원리
모든 MongoDB 드라이버는 세 가지 주요한 기능을 수행한다.
첫째, 모든 도규먼크의 _id 필드에 디폴트로 저장되는 값인 MongoDB 객체 ID를 생성한다.
두번째, 드라이버는 특정 언어로 표현된 도큐먼트와 MongoDB 이진 데이터 포맷인 BSON 사이의 변환을 수행한다.
셋째, 드라이버는 MongoDB 와이어 프로토콜을 사용해서 네트워크 소켓을 통해 데이터베이스와 통신한다. 소켓 통신의 방식, 특히 소켓에 대한 쓰기를 수행할 때 쓰기에 대한 응답을 기다리는지의 여부는 알아둘 필요가 있다.
객체 ID 생성
모든 MongoDB의 도큐먼트는 프라이머리 키가 필요하다. 각 컬렉션에서 모든 도큐먼트에 걸쳐 유일해야만 하는 이 키는 _id 필드에 의해 참고된다.
MongoDB 객체 ID는 전역적으로 고유하도록 설계되었으며, 특정 맥락에서 고유하도록 보장함을 의미한다.
4바이트 타임스탬프, 3바이트 서버의 ID, 2바이트 프로세스 ID, 마지막 3바이트는 객에 ID가 생성될 때마다 하나씩 증가하는 프로레스의 로컬 카운터.
이러한 ID들이 서버가 아닌 드라이버에서 생성된다는 것을 이해하는 것이 중요하다. 이는 서버상에서 프라이머리 키를 증가시켜 결국 ㅅ버에서 병목현상을 일으키게 되는 기존의 RDBMS와는 다른 점이다.
인덱싱의 이론적 고찰
단순 인덱스
복합 인덱스: 하나 이상의 키를 사용
인덱싱 규칙
- 인덱스는 도큐먼트를 가져오기 위해 필요한 작업량을 많이 줄인다.
- 한 쿼리를 실행하기 위해서 하나의 단일 키 인덱스만 사용할 수 있다.
- a-b에 대한 복합 인덱스를 가지고 있다면 a에 대한 인덱스는 중복이고, b에 대한 인덱스는 중복이 아니다.
- 복합 인덱스에서 키의 순서는 매우 중요하다.
인덱싱의 핵심 개념
단일키 인덱스: 인덱스 내의 각 엔트리는 인덱스 되는 도큐먼트 내의 한 값과 일치한다. ex) _id에 대해 디폴트로 생성되는 인덱스
복합키 인덱스: 각 엔트리가 하나 이상의 키로 구성된 인덱스다. 키는 나타나는 순서로 비교해야 한다.
인덱스 효율: 쿼리 성능을 위해서 인덱스가 반드시 필요하나, 각 인덱스는 유지 비용이 들어간다. 어떤 컬렉션에 10개의 인덱스를 가지고 있다면, 삽입 연산을 할 번 수행할 때마다 도큐먼트 자체의 쓰기 연산 이외에도 10개의 인덱스를 수정해야 한다. 도큐먼트 삭제, 재배치, 인덱스 키 업데이트하는 것과 같은 쓰기 연산에도 해당된다. 읽기 위주의 애플리케이션에서 인덱스 비용은 인덱스로 인해 얻을 수 있는 효과로 상쇄된다. 사용되지 않는 인덱스나 중복된 인덱스가 존재하면 안 된다.
모든 인덱스가 적합하게 만들어졌다고 해도 쿼리를 더 빠르게 처리하지 못할 가능성이 여전히 존재한다. 이것은 인덱스와 현재 작업 중인 데이터를 램에서 다 처리하지 못할 때 발생한다. MongoDB는 운영체제에서 mmap() 시스템 호출을 이용해 모든 데이터 파일을 메모리에 매핑한다. WiredTiger 스토리지엔진은 다르게 작동한다. 이 시점부터는 모든 도큐먼트, 컬렉션, 인덱스를 포함하는 데이터 파일이 페이지라고 부르는 4KB의 청크로 운영체제에 의해 램으로 스왑된다. 해당 페이지에 대한 데이터가 요청될 때 마다 OS는 그 페이지가 램에 있는지 확인해야 한다. 만약 램에 없으면 페이지 폴트 예외를 발생시키고, 메모리 관리자는 해당 페이지를 디스크로부터 램으로 불러들인다.
램이 충분하다면 사용하는 모든 데이터 파일이 메모리에 로드될 것이다. 쓰기 연산에서와 같이 메모리의 데이터가 수정될 경우 이러한 수정사항은 OS에 의해 비동기적으로 디스크에 반영된다. 쓰기 연산은 빠르고 램에서 직접 발생하므로 디스크 액세스 횟수가 최소화된다.
그러나 모든 데이터를 수용하지 못하면 점점 페이지 폴트가 발생하기 시작한다. 이것은 OS가 디스크를 빈번하게 액세스함으로써 읽기와 쓰기 연산이 매우 느려지게 된다는 것을 의미한다. 최악의 경우에는 데이터의 크기가 가용한 램의 크기보다 훨씬 더 커서 모든 읽기와 쓰기에 대해 디스크 액세스를 해야 하는 상황이 발생할 수도 있다. 이런 현상을 스래싱이라고 하는데, 성능을 심각하게 저해한다.
이런 현상을 피하려면 최소한 인덱스만큼은 램에 들어가도록 해야 한다. 이것이 필요없는 인덱스를 만들지 말아야 할 중요한 이유 중 하나이다. 인덱스를 추가로 만들면 인덱스를 유지하기 위해 더 많은 램이 필요하다.
백업: 인덱스는 구축하기 어려우므로 백업을 해놓아야 한다. 불행히도 모든 백업이 인덱스를 포함하지는 않는다. mongodump나 mongorestore를 사용할 수도 있는데, 이 유틸리티는 컬렉션과 인덱스의 정의만을 보관한다. 데이터가 크면 모든 인덱스를 다시 생성하는 시간이 너무 오래 걸려서 인덱스 재구축이 현실적으로 어려울 수도 있다.
백업이 인덱스에 포함하길 원한다면 MongoDB의 데이터 파일 자체를 백업해야 한다.
DEFRAGMENTING: 기존의 데이터의 업데이트나 대량의 데이터 삭제가 자주 발생한다면 인덱스가 심하게 단편화된다. 단편화된 인덱스의 일차적인 징후는 주어진 데이터의 크기보다 인덱스의 크기가 훨씬 더 큰 것이고, 이로 인해 인덱스가 램을 필요 이상으로 많이 사용할 수 있다. 이런 경우에 하나 혹은 그 이상의 인덱스를 재구축하는 것을 고려해 볼 수 있다. reindex 명령을 수행하여 개별 인덱스를 삭제하고 재생성함으로써 해결할 수 있으나 재구축 동안 쓰기 잠금을 한다.
쿼리 최적화
느린 쿼리 분석
MongoDB 프로파일러를 사용하면 느린 쿼리를 쉽게 찾을 수 있다. 하지만 그 쿼리가 느린 이유를 발견하는 것은 까다롭고 몇 가지 추리력이 필요하다. 문제가 되는 쿼리에 대해 explain을 실행해 봄으로써 확실한 것을 발견할 수 있다.
EXPLAIN()의 사용과 이해
explain 명령은 주어진 쿼리의 경로에 대한 자세한 정보를 제공한다. cursor 필드를 통해 알 수 있는데 BtreeCursor라면 인덱스를 탄 것이다.
scanAndOrder 필드는 쿼리 옵티마이저가 인덱스를 사용하지 못하고 정렬된 결괏값을 반환할 때 나타난다. 따라서 이 경우에 쿼리 프로세스는 컬렉션을 스캔해야 할 뿐만 아니라 결괏값도 직접 정렬해야 한다.
MongoDB 쿼리 옵티마이저
쿼리 옵티마이저는 해당 쿼리를 가장 효율적으로 실행하기 위해 어떤 인덱스를 사용할지 결정하는 소프트웨어다. 다음 규칙을 따른다.
1. scanAndOrder를 피한다. 즉, 쿼리가 정렬을 포함 하고 있으면 인덱스를 사용한 정렬을 시도한다.
2. 유용한 인덱스 제한 조건으로 모든 필드를 만족시킨다. 즉, 쿼리 셀렉터에 지정된 필드에 대한 인덱스를 사용하도록 노력한다.
3. 쿼리가 범위를 내포하거나 정렬을 포함하면 마지막 키에 대한 범위나 정렬에 도움이 되는 인덱스를 선택한다.
플러그형 스토리지 엔진 API
스토리지 엔진은 데이터베이스와 하드웨어 간의 인터페이스다. 스토리지 엔진은 셸 또는 드라이버 쿼리를 수행하는 방법을 변경하지 않으며, 클러스터 수준에서 MongoDB에 개입하지 않는다. 그러나 스토리지 엔진은 데이터를 디스크에 기록, 디스크에서 삭제 및 디스크에서 읽는 방법과 데이터 저장에 사용할 데이터 구조에 간섭하게 된다.
플러그형 스토리지 엔진 API를 사용하면 타사에서 MongoDB용 스토리지 엔진을 개발할 수 있다. 플러그형 스토리지 엔진 API 이전에 MongoDB에서 사용할 수 있는 스토리지 엔진은 MMAPv1뿐이었다.
MMAPv1 스토리지 엔진은 메모리 매핑을 기반으로 하며, MongoDB에 대한 안정적인 솔루션이다. 저장해야 할 데이터가 많은 경우 눈에 띄는 MMAPv1의 한 가지 단점은 데이터 세트가 증가함에 따라 필요할 때마다 2GB 블록을 사전 할당하는 정도까지 엄청난 양의 디스크 공간을 빠르게 소비한다는 것이다.
버전 3.0부터 MongoDB에 다른 스토리지 모듈을 사용하도록 지시할 수 있으며, 이것이 플러그형 스토리지 엔진 API의 기능이다. MongoDB 3.0에는 MMAPv1에 대한 대안, 즉 WiredTiger가 번들로 제공된다.
왜 다른 스토리지 엔진을 사용할까?
데이터베이스 저장 및 액세스 측면에서 서로 다른 요구사항을 갖고 있다.
뉴스 사이트는 소셜 미디어 사이트와 비교하여 조사할 데이터가 훨씬 적으며, 많은 방문객에게 첫 페이지는 동일하게 보인다. 반면에 소셜 미디어 사이트는 셀 수 없이 수많은 트윗이나 상태 업데이트를 거쳐야 한다.
뉴스 사이트 애플리케이션은 맴키시디, 레디스와 같은 외부 메모리 캐시 시스템을 사용하여 동일한 데이터를 고속으로 전달할 수 있다. 그러나 방문자마다 매번 다른 소셜 미디어 사이트에는 도움이 되지 않는다.
이러한 다양한 종류의 시스템을 처리하기 위해 MongoDB는 플러그형 스토리지 엔진 개념을 구현하여 데이터베이스 관리자 또는 시스템 엔지니어가 사용 사례에 가장 적합한 성능을 제공하는 스토리지 엔진을 선택할 수 있도록 했다.
WiredTiger
WiredTiger는 멀티 코어 확장성과 최적의 햄 사용에 초점을 맞춘 고성능의 확장 가능한 오픈소스 데이터 엔진이다. hazard pointers 및 lock-free algorithms와 같은 최신 프로그래밍 기술을 사용하여 이루어지므로 대체 엔진보다 각 CPU 코어에서 더 많은 작업을 수행할 수 있다.
* hazard pointers: 멀티스레딩 프로그래밍에서는 어떤 메모리 블록이 스레드에 의해 액세스될 수 있어야 하는지 그리고 어떤 스레드가 액세스하는지 여부를 추적하는 것이 중요하다. hazard pointers는 스레드가 액세스하는 메모리 블록에 대한 포인터의 목록이며, 다른 스레드는 hazard pointer 목록에 있는 한 포인터와 해당 포인터가 가리키는 메모리 블록을 수정하거나 삭제할 수 없다.
* lock-free algorithms: 멀티 스레드 프로그래밍에서 중요한 개념이다. lock-free 알고리즘은 여러 스레드가 서로 잠금을 해제하기를 기다리고 있는 상황 때문에 프로그램이 멈추는 상황을 피하고, 프로그램 전체가 진행되도록 보장하기 위한 프로그래밍 패턴이다.
WiredTiger로의 전환
WiredTiger를 사용하기 전에 반드시 64비트 시스템을 실행해야 한다. WiredTiger를 사용하기 위해 MongoDB를 설정할 때 새로운 dbpath 디렉터리에서 WiredTiger 구성으로 MongoDB 서버를 시작하는 것이 중요하다. MMAPv1 구조에 있는 dbPath로 서버를 시작하면 시작되지 않을 것이다. 서로 호환되지 않으며, 저장 구조 간에 사용 가능한 on-the-fly 변환이 없기 때문이다. mongodump와 mongorestore를 통해서 마이그레이션하는 방법이 있다.
MongoDB 설치에서 WiredTiger를 활성화하기 위해서 해야 할 일은 기본 YAML 설정 파일에 스토리지 구성을 설정하는 것이다.
MMAPv1과의 비교
WiredTiger의 성능은 MMAPv1을 사용하는 MongoDB 인스턴스와 어떤 차이가 있을까?
자바스크립트 및 셸 스크립트를 사용하여 MMAPv1에 대해 세 개의 WiredTiger를 구성을 테스트한다.
- MMAPv1을 사용한 기본 구성
- WiredTiger, 압축 안함
- snappy 압축이 있는 WiredTiger
- zlib 압축이 있는 WiredTiger
zilb은 Huffman 코딩을 사용하는 LZ77의 변형인 DEFLATE 압축 알고리즘의 추상화다. zlib은 많은 소프트웨어 플랫폼에서 매우 일반적이며, gzip 파일 압축 프로그램의 기초가 된다. snappy는 구글에서 개발했으며 BigTable과 같은 구글 프로젝트에서 널리 사용된다. snappy는 중간 수준의 솔루션에 가깝고, 최대한 압축을 목표로 하지는 않지만, 대신 합리적인 수준의 압축을 고속으로 진행할 수 있다.
WiredTiger는 MMAPv1보다 삽입 작업에서 디스크 사용이다. WiredTiger 스토리지 엔진 구성의 디스크 사용량이 10배 가량 적으며 삽입 작업에서는 거의 대등한 성능을 보인다.
읽기 작업은 MMAPv1 구성에서 WiredTiger 구성보다 10초 이상 오래 걸린다. 후속 반복이 결과를 메모리 캐시에서 직접 가져가므로 첫 번째 반복이 가장 오래 걸린다. 콜드 페치의 경우 MMAPv1이 가장 느리다.
디스크 사용면에서 WiredTiger가 엄청난 이득이 있다. 캐시 미스가 자주 발생하는 소셜 미디어는 콜드 페치의 속도가 중요하다.
WiredTiger는 읽기 및 쓰기 작업 모두에서 성능이 뛰어나며, 도큐먼트 수준의 잠금 기능을 제공한다.
MMAPv1은 컬렉션 수준의 이상의 잠금을 제공하지 않는다.
MMAPv1는 몽고디비 4.2버전부터 deprecated되었다.
복제 개관
replica set은 노드 그룹이 자동으로 데이터를 동기화하고 노드가 사라지면 장애조치를 수행하도록 구성한다.
복제의 중요성
모든 데이터베이스는 장애를 겪을 수 있고, 복제는 그러한 장애에 대한 일종의 보험이다.
장애의 예시
- 애플리케이션과 데이터베이스 사이의 네트워크 단절
- 호스팅 제공 업체가 가끔 서버를 다운시키는 경우
- 전렬 손실
- 서버의 하드디스크 장애
복제 노드는 백업과는 다름. 백업은 특정 시간의 스냅샷이다. 복제본은 항상 최신 상태이다.
복제의 사용 예와 한계
복제는 일차적으로 중복성을 위해 설계된다. 복제 노드는 프라이머리 노드와 동기화된 상태를 유지. 복제는 비동기적이므로 노드 사이의 어떠한 종류의 네트워크 지연이나 장애도 프라이머리 노드의 성능에 영향을 끼치지 못한다.
복제의 또 다른 용례는 장애복구다.
중복과 장애복구 외에도 복제는 리소스를 많이 사용하는 연산을 프라이머리 외의 다른 노드에서 실행함으로써 데이터베이스 관리 작업을 단순하게 해준다. 백업을 세컨더리 노드에서 수행하는 것이 일반적인 관행이다. 대용량 인덱스 구축의 경우 세컨더리에서 구축한 후 프라이머리로 변경하고 이전의 프라이머리 노드에 대해 인덱스를 구축한다.
읽기 위주의 애플리케이션에서 복제는 로드 밸런스를 해준다.
replica set은 다음과 같은 조건에서는 도움이 되지 않는다.
- 할당된 하드웨어가 주어진 일을 처리할 수 없을 때. 예로 작업 데이터가 가용 램보다 많을 경우 세컨더리 노드로 읽기요청을 하면 기대했던 만큼의 성능 향상을 이뤄지지 않을 것이다. 이 시나리오에서 성능은 디스크가 처리할 수 있는 초당 입출력 연산 횟수에 따라 제한된다. 복제를 읽으면 총 IOPS가 증가하지만 IOPS가 100에서 200으로 늘어나도 성능 문제가 해결되지 않을 수 있다. 쓰기가 동시에 발생하고 그 수의 일부를 소비하는 경우에 특히 그렇다. 샤딩이 더 나은 옵션이 될 것이다.
- 읽기에 대한 쓰기의 비율이 50%를 초과하는 경우. 프라이머리에 대한 모든 쓰기 연산이 결국 세컨더리 노드에도 적용되어야 한다.
- 애플리케이션에서 일관성이 요구되는 읽기가 필요할 때. 세컨더리 노드는 비동기적으로 복제를 수행하므로 프라이머리의 마지막 쓰기를 반영하지 못할 수도 있다. 몇 시간이나 지연될 수 있다.
복제 세트
셋업
replica set에 대해 권장되는 최소의 구성은 세 개의 노드인데, 이는 노드가 두 개인 replica set에서 프라이머리 서버가 다운될 경우 과반수를 확보할 수 없기 때문이다. 3-멤버 replica set에는 데이터를 보유하는 세 멤버 또는 데이터를 보유한 두 멤버와 아비터를 보유한 하나의 멤버가 있을 수 있다. replica set 멤버는 투표를 통해 새로운 마스터를 선출하는 프로세스를 거친다.
처음의 두 노드는 가장 중요하고 지속성을 갖는 mongod 인스턴스다. 둘 중 어느 것도 복제 세트의 프라이머리로 가능할 수 있고, 둘 다 완전히 복제된 데이터를 갖는다. 아비터는 복제 데이터를 가지고 있지는 않지만, 일종의 중립적인 관찰자의 기능을 갖는다.
아비터는 프라이머리 선출에 참여하지만, 그 어떤 데이터도 복제하지 않는 경량의 mongod 서버다.
복제 작동 방식
replica set은 oplog와 heartbeat에 의존한다. oplog로 데이터를 복제하고, heartbeat로 헬스체크하여 장애복구를 할 수 있다.
오피로그
MongoDB 복제의 중심에서 oplog가 있다. 데이터에 대한 모든 수정 사항을 기록한다. 클라이언트가 프라이머리 노드에 대해 쓰기를 할 때마다 해당 쓰기를 세컨더리에서 재생하기 위한 충분한 정보가 프라이머리 노드의 오피로그에 자동으로 추가된다.
그러면 세컨더리가 어떻게 오피로그에서의 위치에 대한 정보를 계속해서 유지할 수 있을까? 세컨더리 역시 오피로그를 가지고 있어 가능하다. 쓰기가 기록되고 프라이머리 노드의 오피로그에 추가된다. 곧이어 모든 세컨더리 노드도 자신의 오피로그에 프라이머리 노드의 오피로그를 복제한다. 따라서 해당 세컨더리 노드가 업데이트할 준비가 되었을 때 세 가지 일을 수행한다.
1. 자신의 오피로그에서 가장 최근 항목의 타임스탬프를 검사.
2. 프라이머리 노드의 오피로그에서 이 타임스탬프 이후의 모든 항목을 질의.
3. 데이터 쓰기를 수행하고 이들 항목들을 자신의 오피로그에 추가.
세컨더리는 프라이머리의 오피로그로부터 새로 추가도니 항목을 즉각 적용하기 위해 롱 풀링을 사용한다. 롱 풀링은 세컨더리가 프라이머리에 장기간 요청을 보내는 것을 뜻한다. 프라이머리가 수정 사항을 수신하면 즉시 대기 요청에 응답한다. 네트워크 장애 또는 세컨더리 노드 자체의 유지 보수 작업 때문에 최신 데이터를 갖지 못하게 되면 각 세컨더리 오피로그에서 가장 최근의 타임스탬프를 이용해서 아직 업데이트되지 못한 복제를 모니터할 수 있다.
복제 중지
세컨더리가 프라이머리의 오피로그에서 동기화할 시점을 발견하지 못하면 복제는 완전히 중지된다.
오피로그 크기 선정
오피로그의 크기를 조정하려면 mongod 인스턴스를 중지하고, 독립적인 인스턴스로 시작하고, 오피로그 크기를 수정한 후 멤버를 다시 시작해야 한다.
디스크 공간의 5%면 오피로그의 크기로 충분하다.
시간당 생성되는 오피로그의 양이 어느 정도인지 알게 되면 오피로그의 크기를 얼마나 할당해야 하는지를 결정할 수 있다.
하트비트와 장애복구
replica set의 각 멤버는 디폴트로 다른 멤버들을 매 2초마다 핑을 해본다.
모든 노드가 건강하고 반응이 있으면 복제 세트는 잘 수행되고 있는 것이다. 하지만 어느 한 노드라도 반응이 없다면 조치를 취해야 한다. 모든 복제 셋의 목적은 어느 때든지 정확히 하나의 프라이머리 노드가 존재하도록 하는 것이다. 이것은 과반수의 노드가 살아있을 때 가능하다.
과반수가 존재하지 않을 때는 프라이머리는 실제로는 세컨더리로 강등된다.
커밋과 롤백
복제 셋에 대해 이해해야 할 마지막 한 가지 중요한 점은 커밋이다. 단일 도큐먼트에 대한 작업은 원자적이지만, 여러 도큐먼트를 포함하는 작업은 전체적으로 원자적이지 않다.
취소된 쓰기는 rollback이라는 서브디렉토리에 저장된다.
관리
장애조치와 복구
장애조치의 규칙을 요약하고 복구와 관련된 몇 가지 제안사항을 살펴보도록 하겠다. 복제 셋은 설정된 모든 멤버가 서로 통신할 수 있을 때 온라인 상태가 된다. 각 노드는 디폴트로 한 표씩 주어지고, 이 표는 과반수를 형성하고 프라이머리를 선정하는 데 사용된다.
깨끗한 장애는 네트워크 장애를 들 수 있다.
확실한 장애는 데이터 파일이 깨진 상태에서 재개된다. mongod 프로세스가 저널링이 사용되지 않은 상태에서 불시에 셧다운되거나 하드 디스크가 깨진 경우 모든 이런 유형의 장애에 속한다. 재동기화를 통해 데이터 파일을 완전히 대치하거나 최근의 백업에서 복구하는 것이다.
배포 전략
복제 셋은 50개의 노드까지 구성될 수 있다.
드라이버와 복제
연결과 장애조치
MongoDB의 드라이버에서는 마스터와 슬레이브 그리고 복제 세트에 연결하는 것이 비교적 일관적으로 이루어진다.
단일 노드 연결
복제 셋은 하나의 노드에 연결하는 옵션이 언제나 존재한다. 복제 셋의 마스터로 지정된 노드에 연결하는 것과 독립적인 MongoDB 노드로 연결하는 것 사이에는 차이점이 없다. 두 경우 모두 드라이버는 TCP 소켓 연결을 싲가하고 isMaster 명령을 실행한다. 독립 노드에서 이 명령은 isMaster: true를 반환한다.
드라이버에서 가장 중요한 것은 isMaster 필드를 true로 지정하는 것인데, 이것은 해당 노드가 독립적이거나 복제 셋에서 프라이머리 노드임을 나타낸다.
샤딩을 통한 시스템 확장
데이터베이스를 작은 조각으로 분할하여 단일 머신이 모든 데이터를 저장하거나 전체 로드를 처리하지 않도록 하기 위해 고안된 것이다.
MongoDB의 샤딩은 애플리케이션에 투명한데, 이는 샤딩 클러스터를 쿼리하기 위한 인터페이스가 복제 셋 또는 단일 mongod 서버 인스턴스를 쿼리하기 위한 인터페이스와 완전히 동일하나는 것을 뜻한다.
샤딩 개관
실제로 언제 샤딩이 정확한 해결책이 될 수 있을지를 알 수 있는 방법에 대해 설명한다.
샤딩이란 무엇인가?
샤딩은 대규모 데이터 셋을 작고 관리하기 쉬운 조각으로 분할하는 과정.
데이터 크기가 커지고 애플리케이션이 더 많은 읽기-쓰기 처리량을 필요로 할 때.
충분한 램이 없거나 CPU 코어가 부족할 경우. 디스크 용량이 부족할 경우.
이는 관리 및 성능 오버 헤드를 추가하는 복잡한 시스템이므로 샤딩이 애플리케이션에 필요할 것인지 확인해야 한다.
언제 샤딩을 할 것인가?
샤딩은 저장소 분산과 부하 분산이라는 두 가지 주요한 목적을 가지고 있다.
저장소 분산
시스템의 저장소 요구사항을 이해하는 것은 어렵지 않다. 저장소 용량을 주의깊게 모니터링하면 애플리케이션이 늘어남에 따라 애플리케이션에 필요한 저장소가 하나의 노드 용량을 언제 초과하는지 명확하게 알 수 있다. 더 많은 용량을 추가할 수 없는 경우 샤딩이 최선의 방법일 수 있다.
부하 분산
시스템에서 지원해야 하는 부하 - 클라이언트의 요청에 의해 사용된, CPU, 램 및 I/O 대역폭에 대한 이해는 약간 미묘한 차이가 있다. 인덱스와 작업 데이터셋을 램에 유지하는 것이 중요하다고 이야기했다. 이것이 샤딩의 가장 큰 이유 중 하나다. striped RAID 구성으로 디스크를 배열하면 디스크에서 처리할 수 있는 초당 연산 횟수가 늘어가므로 데이터 대 램 비율을 높일 수 있게 해준다.
디스크가 100%까지 가득 차고 모든 시스템에 과부하가 걸릴 때까지 샤딩의 수행을 미룰 생각을 할 수도 있겠지만, 좋은 생각이 아니다.
네트워크 사용, 디스크 샤용 작업 데이터 대 사용 가능한 램의 비율을 정기적으로 분석한 결과에 바탕을 두어야 한다.
샤드 클러스터 구성 요소의 이해
샤드 클러스터의 구성요소를 살펴보자.
- 샤드는 애플리케이션 데이터를 저장한다. 샤드 클러스터에서는 오직 mongos 라우터 또는 시스템 관리자만 샤드에 직접 연결해야 한다. 비샤드 배포와 마찬가지로 각 샤드는 개발 및 테스트를 위한 단일 노드일 수 있지만, 운영환경의 복제 셋이어야 한다.
- mongos 라우터는 클러스터 메타데이터를 캐시하고 이를 사용하여 올바른 샤드에 작업을 라우팅하거나 샤딩한다.
- 설정 서버는 클러스터에 대한 메타데이터를 영구적으로 저장하며, 여기에는 어떤 샤드가 어떤 데이터 하위 집합을 가지는지가 포함된다.
샤드: 애플리케이션 데이터의 저장소
샤드는 애플리케이션 데이터가 샤드된 클러스터에 저장되는 유일한 장소다.
mogos 라우터: 연산 라우터
각 샤드에는 클러스터 데이터의 일부만 들어 있으므로 연산을 적절한 샤드로 라우팅해야 한다. 그것이 mogos가 들어오는 곳이다. mongos 프로세스는 모든 읽기, 쓰기 및 명령을 해당 샤드에 보내는 라우터다. 이런 방식으로 mongos는 클라이언트에게 클러스터와의 단일 접촉 지점을 제공하며, 샤드된 클러스터가 비샤드 클러스터와 동일한 인터페이스를 제공할 수 있게 한다.
mongos는 지속성이 없는 경량 프로세스다. 이러한 이유로 애플리케이션 서버와 동일한 시스템에 배포되며, 특정 샤드에 대한 요청에는 하나의 네트웤 홉만 필요하다. 애플리케이션은 로컬 mongos에 연결하고, mongos는 개별 샤드에 대한 연결을 관리한다.
설정 서버: 메타데이터의 저장소
mongos 프로세스는 지속성이 없으며 이는 클러스터를 적절하게 관리하는 데 필요한 메타데이터를 영구적으로 저장해야 함을 뜻한다. 설정 서버가 이 역할을 맡고, 글로벌 클러스터의 설정사항과 각 데이터베이스, 컬렉션, 각 범위의 데이터의 위치 그리고 샤드 간 데이터의 전송 내역을 가지고 있는 변동사항 로그다.
mongos 프로세스는 시작할 때마다 설정 서버로부터 메타데이터 복사본을 가지고 온다. 따라서 설정 데이터가 없으면 샤드 클러스터에 대한 일관점 관점을 가질 수 없는데, 이런 점을 설정 서버가 설계된 방법에 몇 가지 영향을 끼쳤다.
mongos 프로세스가 쓰기를 할 때 2단계 커밋을 사용하는데, 이것을 통해 설정 서버들 사이의 일관성이 보장된다. 실제 서비스 시스템에서는 정확히 세 개의 설정 서버를 실행해야 하고, 중복성을 위해 서버 다른 별개의 서버에서 호스트해야 한다.
샤드된 클러스터에서의 데이터 분산
샤딩을 수행하기 위한 다른 여러 가지 장법을 논의하기 전에 MongoDB에서 데이터를 그룹화하고 구성하는 방법에 대해 알아보자. 몽고디비에는 도큐먼트, 청크, 컬렉션 및 데이터베이스 등 네 가지 단위 수준이 있다.
- 도큐먼트 - 가장 작은 데이터 단위. 시스템의 단일 객체를 나타내며 더 이상 분할할 수 없다. 관계형디비의 행과 비교할 수 있다.
- 청크 - 필드의 값별로 클러스터된 도큐먼트 그룹이다. 청크는 샤드 설정에만 존재하는 개념이다. 이는 샤드 키라는 필드 또는 필드 집합에 대한 값을 기반으로 하는 도큐먼트의 논리적 그룹이다.
- 컬렉션 - 데이터에비스 내에 명명된 그룹화된 도큐먼트다. 데이터베이스를 애플리케이션에 적합한 논리적 그룹으로 분리할 수 있도록 컬렉션 개념을 제공.
- 데이터베이스 - 도큐먼트 모음을 포함한다. 시스템 최상위 레벨 그룹
샤드된 클러스터에 데이터를 분산시킬 수 있는 방법
이제 MongoDB에서 데이터가 논리적으로 그룹화되는 여러 가지 방법을 알게 되었다. 다음 질문은 이것이 샤딩과 어떻게 상호작용하는가다.
- 전체 데이터베이스의 수준에서 각 데이터베이스가 모든 컬렉션과 함께 자기 자신의 샤드에 위치한다.
- 컬렉션의 파티션 또는 청크 수준에서 컬렉션의 도큐먼트 자체가 분할되어 도큐먼트의 샤드 키라는 필드 또는 필드 집합의 값에 따라 여러 샤드에 분산된다.
샤드에 데이터베이스 분산 처리
데이터베이스 분산을 위한 실제 애플리케이션의 한 예로 서비스로의 MongoDB가 있다. 이 모델의 한 구현에서 고객은 단일 MongoDB 데이터베이스에 대한 액세스 비용을 지불할 수 있다. 백엔드에서는 각 데이터베이스가 샤드된 클러스터에 생성된다. 즉, 각 클라이언트가 대략 동일한 양의 데이터를 사용하면 클러스터 전체에 데이터베이스가 분산되므로 데이터 분산이 최적이 된다.
컬렉션 내에서 샤딩하기
몽고디비 자체가 모든 파티셔닝 결정을 내리는 샤딩의 형태
개별 컬렉션의 파티셔닝을 허용하기 위해 MongoDB는 이전에 본 것 처럼 미리 정의된 필드의 값 또는 샤드 키라는 필드 셋을 기반으로 도큐먼트의 논리적 그룹인 청크의 개념을 정의한다.
코어에서 몽고디비의 샤딩은 벙위 기반이다. 각 '청크'는 샤드 키의 범위를 나타낸다. MongoDB는 자신이 속한 청크를 결정하기 위해 도큐먼트를 볼 때 먼저 샤드 키의 값을 추출한 다음, 주어진 샤드 키 값이 샤드 키 범위에 들어 있는 청크를 찾는다.
샘플 샤드 클러스터 구축하기
샤드된 클러스터를 구축하는 전 과정은 3단계로 구성된다.
1. mongod 및 mongos 서버 시작 - 클러스터를 구성하는 모든 mongod 및 mongos 프로세스를 생성하는 것이다.
2. 클러스터 구성 - 다음 단계는 복제 세트가 초기화되고 샤드가 클러스터에 추가되도록 구성을 업데이트하는 것이다. 이후에 노드는 모두 서로 통신할 수 있다.
3. 샤딩 컬렉션 - 마지막 단계는 컬렉션을 여러 샤드에 분산시킬 수 있도록 컬렉션을 분할하는 것이다. 이것이 별도의 단계로 존재하는 이유는 MongoDB가 동일한 클러스터에 샤드 컬렉션과 비샤드 컬렉션 둘 다 가질 수 있어서 명시적으로 어떤 것을 샤딩할지를 선언해야 하기 때문이다.
mongod와 mongos 서버 시작하기
두개의 샤드와 세 개의 설정 서버로 구성.
'Software Development > Database' 카테고리의 다른 글
[데이터 중심 애플리케이션] 복제(분산 데이터) (0) 2021.10.16 [데이터 중심 애플리케이션] 저장소와 검색 (0) 2021.09.05 [Oracle] 오라클 - Function 생성 및 실행 방법 (0) 2020.10.15 [Database] 인덱스 자세히 알아보기 (0) 2020.08.19 ORA-01654 인덱스를 확장할 수 없습니다 (0) 2020.07.09