DB/MongoDB

[MongoDB] MongoDB 개요

꾸준함. 2023. 11. 26. 02:29

개요

현재 진행하고 있는 프로젝트에서는 관계형 데이터베이스와 cache 목적으로 사용하는 Redis만 사용하고 있습니다.

아무래도 agile 방법론을 도입하여 개발을 진행하다 보니 데이터베이스 스키마가 지속적으로 변경하는데 제공하는 기능으로 데이터 export/import가 있다 보니 스키마가 변경될 때마다 소스 코드를 직접 수정하고 변경된 DB 스키마를 local, staging, dev 그리고 prod 환경에 모두 반영해야 하는 애로사항이 발생했습니다.

이에 따라 저희 팀에서는 schema가 비교적 자유롭고 고가용성과 수평 확장이 용이한 MongoDB를 도입하는 것을 고려 중이고 내년부터 도입할 것 같아 이 게시물을 통해 MongoDB에 대해 간단히 알아보도록 하겠습니다.

 

SQL vs NoSQL

MongoDB는 대표적인 NoSQL 데이터베이스 중 하나이기 때문에 SQL과 NoSQL의 차이를 알아볼 필요가 있습니다.

 

SQL

 

SQL은 관계형 데이터베이스에 쓰이는 문법으로 프로젝트에서 많이 쓰이는 Oracle, MySQL, PostgreSQL과 같은 데이베이스에 쓰입니다.

SQL의 경우 테이블을 정의할 때 DDL을 통해 아래와 같이 명확하게 스키마를 정의해야 하며 컬럼이 추가, 수정 혹은 삭제가 되어 스키마가 변경되었을 때 해당 테이블에 변경사항을 반영해야 합니다.

 

create table if not exists member (
    id bigint not null auto_increment,
    created_at datetime(6),
    created_by varchar(255),
    modified_at datetime(6),
    modified_by varchar(255),
    name varchar(255),
    team_id bigint,
    primary key (id)
) engine=InnoDB;

alter table member
    add constraint FK_TEAM_ID
        foreign key (team_id)
            references team (id);

 

관계형 데이터베이스를 사용했을 때 장단점은 아래와 같습니다.

 

장점

  • 정규화를 통해 데이터 중복 방지 가능
  • 기본적으로 정규화를 하기 때문에 Join 성능 좋음
  • 복잡하고 다양한 쿼리 작성 및 호출 가능
  • 스키마에 맞지 않는 데이터를 입력하려고 할 때 잘못된 입력 방지 가능
  • 수직 확장(Scale-Up)에 용이

 

단점

  • 테이블이 기본적으로 정규화되어 있다 보니 하나의 레코드를 확인하기 위해 여러 테이블을 Join 하여 가시성이 떨어짐
  • 스키마가 엄격해서 변경에 대한 공수 큼
    • 특히 운용 환경에 반영할 때 애플리케이션에 영향을 줄 수 있기 때문에 트래픽이 적은 새벽 시간에 적용하거나 정기 배포 때 반영
  • 수평 확장(Scale-Out) 설정이 어렵고 확장할 때마다 애플리케이션단에서 수정이 필요
    • MySQL의 경우 수평 확장을 자체적으로 지원하지 않기 때문에 3rd 파티 솔루션을 사용하거나 애플리케이션 단 수정을 통해 수평 확장 가능

 

NoSQL

 

NoSQL은 아래와 같이 크게 네 가지 타입으로 나뉩니다.

  • document-based
  • key-value
  • column-oriented
  • graph-based

 

여기서 MongoDB는 document-based DB에 속하고 Redis의 경우 key-value DB에 속합니다.

NoSQL의 경우 테이블을 정규화하지 않기 때문에 데이터 접근성과 가시성이 좋다는 특징이 있고 정규화를 진행하지 않기 때문에 Join 없이 조회가 가능해서 응답 속도도 대체적으로 SQL보다 빠르다는 장점이 있습니다.

또한, 앞서 언급했다시피 스키마가 유연하기 때문에 데이터 모델을 애플리케이션의 요구사항에 맞게 데이터를 수용할 수 있으며 스키마 변경 공수가 적게 듭니다.

  • MongoDB의 경우 필요에 따라 같은 테이블(컬렉션) 내 필드가 다른 json 데이터가 저장될 수 있습니다.
  • 스키마가 자유로운 것은 맞으나 관련 없는 데이터들끼리 하나의 컬렉션에 모을 경우 관리도 힘들고 성능 저하를 야기할 수 있기 때문에 어느 정도 스키마 설계를 진행해야 함

 

반면, 정규화를 진행하지 않는다는 것은 관계형 데이터베이스 측면에서 해석하면 역정규화를 진행했다는 것과 비슷하고 이는 데이터 중복 발생이 가능하다는 것을 의미합니다.

위 내용을 모두 정리하면 NoSQL을 사용했을 때 장단점을 아래와 같이 정리할 수 있습니다.

 

장점

  • 정규화를 진행하지 않기 때문에 데이터 접근성과 가시성이 좋음
  • 기본적으로 Join 없이 조회가 가능하기 때문에 응답속도가 빠른 편
  • 스키마 변경에 공수가 적게 들고 스키마가 유연해서 데이터 모델을 애플리케이션의 요구사항에 맞게 수용 가능
  • MongoDB의 경우 고가용성(HA)과 샤딩에 대한 솔루션을 자체적으로 지원하기 때문에 수평 확장에 용이
    • 수평 확장 시 소스코드 변경 없어도 됨

 

단점

  • 데이터의 중복 발생 가능성 존재
  • 스키마가 자유롭지만 trade-off가 존재하기 때문에 스키마 설계를 잘해야 함 (관리 측면 그리고 성능 측면을 고려해서)

 

MongoDB의 구조

MongoDB를 설치하면 아래와 같이 기본적으로 생성되는 데이터베이스가 있으며 계정 권한에 따라 보이지 않을 수 있습니다.

  • admin
  • local
  • config

 

admin 테이블의 경우 인증과 권한을 부여하는 역할을 수행하며 DBA가 주로 접근합니다.

local 테이블은 테이블에 반영된 모든 변경사항 로그를 저장하는 oplog를 가지고 있으며 replication 절차에 필요한 정보를 저장합니다. 또한, startup_log와 같은 인스턴스 진단 정보를 저장합니다.

config 테이블은 sharded cluster를 구성했을 때 각 샤드의 정보를 저장합니다.

 

MongoDB 용어

 

MongoDB와 RDBMS 용어는 비슷하면서 다른데 아래 그림을 통해 간단히 요약할 수 있습니다.

 

https://dev.to/meanii/why-use-mongodb-advantages-use-cases-39io

 

 

Cluster와 Database까지는 용어가 같고 MongoDB에서는 RDBMS의 테이블을 컬렉션, row를 document, 컬럼을 필드로 부릅니다.

 

Collection

컬렉션은 RDBMS의 테이블과 비슷한 개념으로 동적 스키마를 갖고 있어 스키마 수정 필요시 필드 값을 자유롭게 추가, 수정 및 삭제가 가능합니다.

또한, RDBMS의 스키마와 달리 엄격하지 않기 때문에 같은 컬렉션 내에도 필드가 다른 데이터들이 존재할 수 있습니다.

하지만 앞서 언급했다시피 NoSQL이 기본적으로 자유로운 스키마를 허용하더라도 관리 및 성능 측면에서 어느 정도 규격화를 할 필요는 있습니다.

이외 특징으로는 MongoDB에서는 컬렉션 단위로 Index를 생성할 수 있으며 샤드로 나눌 수도 있습니다.

 

Document

컬렉션 내 저장되는 데이터를 document라고 하며 데이터는 bson 형태로 저장되고 json 형태로 표출합니다.

또하 데이터를 저장할 때 RDBMS의 기본키 역할을 하는 _id를 디폴트로 ObjectID 타입의 고유한 값이 저장되며 저장할 데이터 내 _id가 직접 명시되어 있을 경우 중복 여부를 판별하여 중복되었으면 예외를 발생시킵니다.

document 저장 시 상위 구조인 데이터베이스나 컬렉션이 없을 경우 이 둘을 먼저 생성하고 document를 저장합니다.

  • RDMS처럼 미리 DDL을 통해 테이블을 생성해야지만 데이터를 저장할 수 있는 구조가 아님

한 가지 주의할 점은 document의 최대 크기는 16MB라는 점입니다.

 

MongoDB 배포 형태

MongoDB는 아래와 같이 세 가지 형태로 운용이 가능합니다.

  • Standalone
  • Replica Set
  • Sharded Cluster

 

Standalone

 

말 그대로 MongoDB를 한 대로 운용하는 경우입니다.

이렇게 운용할 경우 오류로 인해 DB가 내려갈 경우 서비스 운용이 안되기 때문에 로컬 환경에서 테스트 용도 혹은 스터디 용도로만 사용됩니다.

 

Replica Set

 

MongoDB를 세 대 설치해서 운용하는 배포 형태로 고가용성(HA)을 보장하며 제일 많이 사용하는 구조입니다.

 

https://www.mongodb.com/docs/manual/replication/

 

 

MongoDB 세 대 중 하나는 Primary 나머지 둘은 Secondary로 운용이 되며 각자의 역할은 아래와 같습니다.

 

Primary

  • 읽기 및 쓰기를 모두 처리할 수 있는 멤버로 쓰기는 Primary 멤버만 처리
  • 보통 트래픽 분산 차원 목적으로 secondary 멤버에 read preferece 적용해 read 요청은 모두 secondary 멤버 그리고 write 요청은 모두 primary 멤버가 전담하도록 분배
  • Replica Set 내 하나만 존재 가능

 

Secondary

  • 트래픽 분산 목적으로 읽기에 대한 요청 처리만 가능
  • 비동기 복제를 통해 Primary 멤버와 동일한 데이터 셋을 유지
  • Primary와 달리 Replica Set 내 여러 대 존재 가능

 

fail-over

Replica Set은 Redis Sentinel, Redis Cluster처럼 auto fail-over 기능을 제공합니다.

Replica Set 내 멤버들은 서로 하트비트를 보내며 상태를 체크하며 이때 primary 멤버 상태가 DOWN일 경우 과반수의 투표를 얻은 secondary 멤버가 새로운 primary 멤버가 되고 다른 secondary 멤버들은 새로운 primary 멤버의 데이터를 복제합니다.

Redis와 마찬가지로 투표로 인해 새로운 primary가 선정되고 과반수 이상의 표를 받아야 하는 구조이기 때문에 MongoDB를 최소 3대 이상 그리고 가급적 홀수로 운용하는 것을 추천합니다.

 

P-S-S vs P-S-A

Replica Set을 3대 운용할 때 P-S-A 혹은 P-S-A 구조로 운용을 하는데 추천하는 방식은 P-S-S 구조입니다.

여기서 P는 Primary, S는 Secondary, 그리고 A는 Aribter로 Ariber의 경우 데이터를 들고 있지 않고 fail-over 과정에서 투표만 하는 인스턴스입니다.

P-S-A 구조를 지향하지 않는 이유는 secondary나 primary 인스턴스가 죽었을 때 부하가 하나의 인스턴스로만 집중되고 이에 따라 서비스 장애 확률이 높아지기 때문입니다.

따라서 가용할 수 있는 메모리가 적을 경우에만 P-S-A 구조로 운용하고 웬만하면 P-S-S 구조로 운용하는 것을 추천합니다.

 

https://www.mongodb.com/docs/manual/core/replica-set-architecture-three-members/
https://www.mongodb.com/docs/manual/core/replica-set-architecture-three-members/

 

oplog

Replica Set 내 local db 내 존재하며 Primary 멤버가 write 요청을 받을 때마다 변경사항을 oplog에 반영을 합니다.

Secondary 멤버들은 비동기적으로 oplog로부터 데이터를 복사하여 동기화를 진행하는데 아래와 같이 두 가지 케이스가 존재합니다.

  • Secondary 멤버가 Primary 멤버의 oplog 복제
  • Secondary 멤버가 변경사항 앞서 있는 Secondary 멤버의 oplog 복제

 

Sharded Cluster

 

Replica Set의 경우 모든 멤버가 동일한 데이터를 가지고 있는 반면 Sharded Cluster는 샤딩을 통해 데이터를 분산해서 저장하며 각 샤드는 Replica Set으로 구성되어 있습니다.

Sharded Cluster로 구성함으로써 얻을 수 있는 장단점은 아래와 같습니다.

 

장점

  • 샤딩을 통해 데이터를 분산해서 저장하므로 용량의 한계 극복
  • Target Query를 호출했을 때 데이터 규모와 부하가 크더라도 처리량이 좋음
  • 고가용성 보장 (하지만 Replica Set에 비교하면 HA보다는 분산 목적)
  • 수직 확장을 통해  해결 불가능한 문제를 수평 확장으로 해결

 

단점

  • Replica Set에 비해 관리 포인트가 늘어 관리가 비교적 복잡
  • Replica Set과 비교해서 쿼리가 느림
    • 클라이언트로부터 mongos에 요청이 오면 config server를 통해 어떤 shard에 요청을 할지 파악한 뒤 해당 shard에 쿼리 호출하는 과정을 거침
    • Range Query 호출 시 여러 shard를 조회해야 함

 

위와 같은 장단점을 가지기 때문에 Sharded Cluster는 보통 대량으로 늘어나는 data들에 의해 쓰기 부하가 클 때 사용합니다.

 

아키텍처

 

Sharded Cluster는 아래와 같은 아키텍처를 가지며 여기서 mongos는 L4 역할을 수행함과 동시에 각 샤드가 균등하게 data를 가지도록 합니다.

 

https://www.mongodb.com/docs/v2.6/core/sharded-cluster-architectures-production/

 

클라이언트로부터 요청이 들어왔을 때 데이터를 반환하는 과정은 아래와 같습니다.

  • 클라이언트가 mongos에 요청
  • mongos가 shard meta data를 들고 있는 config server를 통해 어떤 shard를 조회할지 파악
  • mongos가 해당 shard에 요청을 보낸 뒤 취합하여 클라이언트에게 결과 반환

 

Sharding 방법

샤딩 방법으로는 아래와 같이 크게 세 가지가 있으며 주로 Hashed Sharding을 사용합니다.

  • Ranged Sharding
  • Hashed Sharding
  • Zone Sharding

 

Ranged Sharding

Sharding Key의 값을 기준으로 분산하는 방법으로 target query를 통해 빠르게 조회가 가능하다는 장점이 있습니다.

다만, 데이터가 균형 있게 분산된다는 보장이 없으며 데이터 분산을 위해 migration 진행 시 서비스에 크게 영향을 끼칠 수 있으므로 데이터가 균등하게 분산된다는 것이 보장되지 않는 이상 추천하는 방식은 아닙니다.

 

Hashed Sharding

Sharding Key의 값을 해싱 후 분산하는 방식으로 분산이 균등하게 잘된다는 장점이 있으며 가장 많이 사용되는 기법입니다.

다만, 범위 조건에서는 broadcast query 호출 시 여러 샤드로부터 데이터를 가져오므로 target query보다는 느리다는 단점이 있습니다.

그래도 Sharding Cluster 자체가 분산을 목적으로 하기 때문에 가장 추천하는 방법입니다.

 

Zone Sharding

글로벌 서비스의 경우 ip 기반으로 즉, 지역별로 데이터를 분산해야 하는데 이때 사용되는 방법입니다.

 

비고

데이터 샤딩 시 데이터들은 논리적인 단위인 chunk 단위로 분산이 됩니다.

또한 Sharded Cluster를 구성했다고 해서 반드시 모든 collection을 샤딩할 필요 없습니다.

데이터가 분산되어 있으면 하드웨어의 한계를 극복할 수 있다는 장점이 있지만 조회 속도가 저하된다는 단점이 존재하기 때문에 조회가 자주 되는 메타성 데이터의 경우 오히려 sharding을 안 하는 것이 나은 선택일 수 있습니다.

 

정리

 

정리하면 아래와 같습니다.

  • standalone 방식은 스터디용 혹은 테스트 용도로만 사용
  • Replica Set의 경우 장애 발생 시 문제 원인 파악 및 복구가 쉽고 서버 수가 적게 들어 서버 비용이 적게 들고 개발 시 설계가 용이하다는 장점이 있기 때문에 웬만하면 Replica Set으로 운영하는 것을 추천
  • 하지만 증권사처럼 실시간으로 주가가 바뀌어 대량의 Write 처리 시 Scale-Out이 가능하고 Write에 대한 분산이 가능한 Sharded Cluster 추천
    • 즉, 서비스의 요구사항이 Replica Set으로 충족하지 못할 경우에만 Sharded Cluster로 배포

 

Storage Engine

Storage Engine이란 데이터가 메모리와 디스크에 어떻게 저장하고 읽을지 관리하는 컴포넌트이며 MySQL과 동일하게 Plugin 형태로 제공됩니다.

MongoDB도 다양한 Storage Engine을 사용할 수 있는데 MongoDB 3.2 버전부터는 기본 Storage Engine으로 WiredTiger를 제공합니다.

이전 버전에서는 MMAPv1을 Storage Engine으로 제공했었는데 이때 데이터 압축 알고리즘도 제공하지 않고 DB 혹은 Collection 레벨의 Lock만 지원했기 때문에 트래픽이 높은 서비스에서는 MongoDB를 사용하기 쉽지 않았습니다.

하지만 WiredTiger의 경우 데이터 압축 알고리즘을 제공하여 데이터를 4~6배 압축 가능하고 Document 레벨의 Lock을 제공함으로써 RDBMS와 함께 TPS가 높은 서비스에서 사용해도 손색이 없을 정도의 NoSQL DB가 되었습니다.

실제로 데이터베이스 순위를 보면 MongoDB가 NoSQL 데이터베이스 중 가장 높은 랭킹을 차지한 것을 볼 수 있습니다.

 

https://quietbookspace.com/database-ranking-in-october-oracle-mysql/

 

참고

백엔드 개발자를 위한 한 번에 끝내는 대용량 데이터 & 트래픽 처리 초격자 패키지 Online - 비즈니스 요구사항에 유연한 MongoDB

반응형

'DB > MongoDB' 카테고리의 다른 글

[MongoDB] 스키마 모델링 기법  (0) 2023.12.08
[MongoDB] 인덱스  (0) 2023.12.01
[MongoDB] Read/Write 제어  (0) 2023.11.29