1. MongoDB
데이터베이스 트렌드
- 기존의 데이터베이스 관리 시스템은 RDBMS가 중심, but 구글이나 페이스북과 같은 글로벌 서비스를 제공하는 회사가 늘어나면서
방대한 양의 데이터를 충분히 빠른 속도로 처리할 수 있는 데이터베이스에 대한 필요성이 생김. - HBase, 카산드라 등장 -> 자바 언어로 개발되어 GC가 부담, 또한 클러스터 구축을 위한 컴포넌트가 너무 많아 관리 및 트러블 슈팅이 상대적으로 어려움.
- MongoDB는 온라인 서비스에 필요한 블록 캐시, 보조 인덱스, 동시성 처리를 위한 스킵리스트, 하자드 포인터 등의 특성을 가지고 있음. WiredTiger 스토리지 엔진을 주로 사용 (아직 무슨말인지 잘 모르겠네요 ㅠ)
MongoDB vs RDBMS
- MongoDB는 쿼리 결과로 커서를 반환하는데 커서를 통해 도큐먼트를 반복적으로 가져올 수 있음 -> 쿼리의 결과를 서버의 메모리에 모두 담아두지 않아도 처리할 수 있게 하기 위함 (지정된 페이지 사이즈 단위로 가져옴)
- MongDB의 특징
- NoSQL : SQL 인터페이스 사용 x. 외래키를 명시적으로 지원하지는 않지만 논리적으로 도큐먼트간의 관계를 만들 수 있긴 함
- Schema Free - 테이블의 컬럼 수준에만 적용. 사용할 컬럼을 미리 정의하지 않고 언제든지 필요한 시점에 데이터를 저장할 수 있음.
- 비 관계형 데이터베이스
- 도큐먼트에서 id라는 이름의 필드가 자동으로 그 도큐먼트의 프라이머리 키로 선정
- value의 json 값을 문자열로 그대로 저장하는 것이 아니라 문자열 기반의 JSON 텍스트를 BSON으로 변환해 저장
2. 스토리지 엔진
스토리지 엔진
은 사용자의 데이터를 디스크와 메모리에 저장하고 읽어오는 역할을 담당 (옵티마이저, 서버등의 역할은 MySQL과 비슷해 보임)
MongoDB Storage Engine
- MMAPv1
- 출시부터 존재한 엔진
- WiredTiger
- 3.0부터 도입된 새로운 스토리지 엔진
- In-Memory
- WiredTiger의 변형, 데이터를 디스크에 저장하지 않고 메모리에 보관
- RocksDB
- 페이스북에서 LevelDB를 커스터마이징해 개선한 스토리지 엔진
- TokuDB
- Percona에서 개발중인 엔진
각 스토리지 엔진 비교
기능 | MMAPv1 | WiredTiger | RocksDB | TokuDB |
---|---|---|---|---|
잠금 수준 | 컬렉션 | 도큐먼트 | 도큐먼트 | 도큐먼트 |
데이터 구조 | B-Tree | B-Tree | LSM | Fractal-Tree |
빌트인 캐시 | X(운영체제 캐시를 사용) | O | O | O |
세컨드리 인덱스 | O | O | O | O |
데이터 압축 | X | O | O | O |
인덱스 압축 | X | O | O | O |
암호화 | X | O | X | X |
In-Memory 지원 | X | O | X | X |
컬렉션 파티션 | X | X | X | O |
스토리지 엔진 혼합 사용
- 하나의 인스턴스에서 동시에 여러 스토리지 엔진은 사용 불가
- 인스턴스만 다르면 다른 엔진 사용 가능. ex) 1번 샤드는 WiredTiger 스토리지 엔진 사용, 2번 샤드는 RocksDB 엔진 사용
- MongoDB의 각 샤드와 레플리카 셋의 멤버들은 OpLog에 의해 동기화가 됨에 따라 사실상 각 멤버들의 스토리지 엔진에 의존적이지 않기 때문.
MMAPv1
- 2.6까지는 데이터베이스 단위의 잠금 사용, 이후 3.0부턴 컬렉션 수준의 잠금으로 개선됨
- 내장 캐시 기능이 없어 운영체제의 캐시를 활용 -> 시스템 콜을 활용해야 하기 때문에 오버헤드가 크고 페이지 관리나 더티페이지 관리가 안정적이지 못함
- 데이터 파일은 데이터베이스 단위로 생성됨
MMAPv1 엔진 설정
- 운영체제 캐시를 사용하므로 건들게 거의 없음. 거의 디스크 데이터 파일의 경로나 초기 크기 등을 결정하는 옵션, 리눅스 커널의 파라미터 튜닝이 더 많이 필요
- 기본적으로 64MB, 128MB, ... , 2GB, 2GB, ... 순으로 데이터 파일이 생성 됨.
- 이를 방지하려면
storage.smallFiles
옵션을 활성화시켜야 함 -> 16MB ~ 512MB 까지만 증가
MongoDB 서버 상태 확인
mongostat
명령어를 사용하면 서버의 쿼리 처리량, 메모리 사용량 등의 전체적인 상태를 한눈에 볼 수 있다.
flush
: 몇 번이나 데이터 파일이 디스크에 동기화 됐는지mapped
: 데이터 파일이 리눅스의 페이지 캐시에 얼마나 매핑되어 있는지vsize
: 서버 프로세스가 현재 사용 중인 가상 메모리 공간의 크기faults
: 1초에 몇번이나 페이지 폴트가 발생했는지locked db
: 가장 잠금이 심한 데이터베이스
운영체제 캐시
- 급작스럽게 다른 프로세스에서 메모리를 많이 요구하는 경우 리눅스 서버의 캐시 삭제 현상으로 인해 이미 로딩되어 있던 데이터가 날아가는 경우가 있음
- 최적으로 운영하기 위해 리눅스 커널의 작동 방식과 파라미터 튜닝이 필수적임
페이지 캐시 데이터 읽고 쓰기
페이지 캐시의 데이터 읽기
리눅스의 디스크 읽기는 주변의 일부페이지들을 같이 읽어 들이는 Read-Ahead
방식이다. 이는 대량의 데이터를 읽을 때 유용한 방법인데, MongoDB 서버는 주로 랜덤 읽기 위주의 처리를 필요로 한다. 그래서 꼭 필요한 페이지만 메모리로 적재하면 된다.
일반적으로 리눅스의 Read-Ahead 설정은 128 ~ 256 정도인데 이렇게 되면 무조건 64 ~ 128KB 정도의 데이터를 읽어온다. 하지만, 필요한 데이터는 고작 4 ~ 8KB 정도이기 때문에 설정값을 변경해주는 것이 좋다. MMAPv1 스토리지 엔진의 인덱스는 8KB 단위의 페이지를 사용하므로, 디스크 읽기를 8KB에 맞춰야 한다. 결론적으로, 16 ~ 32 정도의 설정이 적절하다.
명령어 : blockdev --setra 16 /dev/sda
페이지 캐시의 데이터 읽기
사용자가 데이터를 변경하면 리눅스의 페이지 캐시 내용을 먼저 변경한다. 이렇게 되면 메모리 상의 데이터는 변경되었지만, 아직 디스크로 동기화되지 않은 페이지를 더티 페이지
라고 한다.
만약, 이 상태에서 비정상 종료를 하게 되면 데이터는 영구적으로 손실된다. 따라서, 대부분의 데이터베이스는 WAL(Write Ahead Log)를 남겨 서버가 다시 시작되면 손상된 데이터를 복구한다.
변경내역을 바로바로 디스크에 저장하지 않는 이유는 더 많은 쓰기를 모아서 한번에 배치처리를 하기 위함이다. 리눅스 커널은 데이터 동기화를 얼마나 자주 실행할 것인지 조정할 수 있도록 몇 개의 파라미터를 제공하고 있다.
dirty_writeback_centisecs, dirty_expire_centisecs : 더티 페이지를 얼마나 자주 어떤 기준으로 디스크에 기록할 것인지
dirty_ratio, dirty_background_ratio : 페이지 캐시가 디스크로 기록되지 않은 더티 페이지를 얼마나 보유하도록 허용할 것인지
dirty_bites, dirty_background_bytes : 바이트 단위로 더티 페이지의 허용치를 결정