NoSQL은 최대한 단순하면서 많은 데이터, RDBMS는 복잡하면서 무결성이 중요한 데이터에 적합하다고 생각한다.
1. NoSQL이란?
Not Only SQL, SQL 뿐만 아니다라는 의미다. SQL을 사용하는 관계형 데이터베이스가 아닌 데이터베이스를 의미한다.
대표적인 관계형 데이터베이스로는 MySQL, Oracle, PostgreSQL이 있고 NoSQL 진영에는 MongoDB와 Redis 등이 있다.
포스팅의 주제인 MongoDB는, 다음 특징들을 가지고있다.
1-1. Document
MongoDB는 정렬된 키와 연결된 값의 집합으로 이루어진 문서 지향 데이터 모델(Document DB)을 사용하는 데이터베이스 이다. 이러한 유형의 모델을 사용하면 정형 및 비정형 데이터를 보다 쉽고 빠르게 통합할 수 있다는 장점이 있다.
Database > Collection > Document > Field 계층으로 이루어져 있으며, Document는 RDBMS의 Row에 해당한다. 계층은 RDBMS와 유사하다.
이 Document 기반 데이터베이스는 RDBMS와 다르게 자유롭게 데이터 구조를 잡을 수 있다. Redis와의 차이점 중 하나가 MongoDB가 document 형식으로 데이터를 저장하는데 반해, Redis는 Key-value 형식으로 데이터를 저장한다는 것이기도 하다.
한 쌍 이상의 Key 와 value가 pair 로 이루어진 Document 들이 모여 Collection 을 이루고, Collection 들은 Database 안에 포함 되어 있다. 그리고 Database 는 Server 안에 위치한다.
MongoDB는 내부적으로 * BSON을 사용하기 때문에(사람에게 보여질 때에는 JSON 형식을 보여주고, 네트워크로 전송할 때 BSON 형태로 만들어서 저장 또는 전송) Array 데이터나 Nested한 데이터를 쉽게 넣을 수 있다. 또 데이터형과 대소문자를 구별하고 중복된 키를 넣을 수 없다.
{"name" : "apple", "name" : "banana"} -> error!
📍 BSON(Binary JSON)
BSON은 JSON과 동일한 구조지만 Binary 형태로 변경된 구조를 말한다.
JSON Document를 Binary로 인코딩한 포맷. JSON의 단점을 해결하기 위한 방안이다. JSON은 텍스트 기반으로 구문 분석이 느리고, 공간 효율성과 거리가 먼 문제점이 있어서 이를 보완한 개념이다. 또 JSON에서 표현할 수 있는 타입은 boolean, 숫자, 배열인데 BSON 에서는 숫자의 경우 Integer, float, long, decimal 등으로 조금 더 디테일하게 표현할 수 있다. 컴퓨터의 언어에 가까운 2진법에 기반을 둔 표현법이며, JSON과 비교해 BSON은 스토리지 공간과 스캔 속도 모두 효율적으로 설계되어 있다. 즉, JSON구조의 좋은 점은 그대로 가져가면서 기계가 빠르게 읽을 수 있는 binary 형태로 변경하여 저장을 한 것이다.
위 그림처럼 mongoDB에 데이터를 하나 넣어보면, 데이터 구조에서 ObjectId라는 생소한 타입을 볼 수 있다.
📍 _id와 ObjectId
몽고DB에 저장된 모든 도큐먼트는 _id 키를 가진다. _id 키 값은 어떤 데이터형이어도 상관 없지만 ObjectId 가 기본이다. 하나의 컬렉션에서 모든 도큐먼트는 고유한 _id 값을 가지며, 이 값은 컬렉션 내 모든 도큐먼트가 고유하게 식별되게 한다.
ObjectId는 RDBMS의 Primary Key와 같이 고유한 키를 의미하는데-
PK와의 차이점은 Primary Key는 DBMS가 직접 부여한다면 ObjectId는 클라이언트에서 생성한다는 점이다. (이는 MongoDB 클러스터에서 샤딩된 데이터를 빠르게 가져오기 위함인데, Router(mongos)는 ObjectId를 보고 데이터가 존재하는 Shard에서 데이터를 요청할 수 있다.) 참고로 ObjectId를 넣지않고 저장한다면 데이터가 그대로 저장된다.
MongoDB 데이터 조작
데이터를 삽입하는 쿼리를 보면 SQL과는 모습이 많이 다른 것을 알 수 있다. 마치 클래스에서 메서드를 통해 실행하는 모습인데, 이처럼 MongoDB는 객체 조작을 통해 데이터를 관리할 수 있다.
📍 MongoDB 데이터 모델링
Database : Collection의 물리적 컨테이너
Collection : RDBMS에서의 table이라고 생각하면 된다. Document의 그룹, Document의 내부에 위치해 있음. RDBMS와 달리 스키마를 따로 갖고 있지 않음 (동적 스키마로 생성)
Document : nosql을 다른말로 Document Oriented Database라 함. 한개 이상의 key-value 쌍으로 이루어진 구조 일반 rdbms에서는 row or tuple과 유사하다 생각하면 된다.
Key/Field : 일반 RDBMS에서 Column이라 생각하면 된다. 컬럼 명과 저장 값으로 생각하면 쉽다.
1-2. 신뢰성
서버 장애에도 서버가 유동적으로 분담하여 서비스는 계속 동작 유지.
Primary 와 Secondry로 구성된 ReplicaSet 구조로 고가용성을 지원.
몽고디비는 기본적으로 하나의 primary와 두개의 secondary로 ReplicaSet을 구성한다. primary는 데이터 쓰기 요청을 처리하고,secondary는 primary부터 변경된 데이터를 복제한다. 이렇게 데이터를 복제하고 유지함으로서, 하나의 서버에서 장애가 발생하더라도 데이터를 유지할 수 있다. 만일 primary서버에 문제가 생기면, secondary가 primary로 전환되어 서버를 계속 유지시킨다. 그리고 다시 빈 secondary서버를 몽고디비가 복구 시켜줌으로서 서버를 유지할수 있게 된다.
1-3. 확장성
- 데이터와 트래픽 증가에 따라 수평확장(scale-out) 가능
- 데이터를 샤딩하여 수평확장(scale-out) 할 수 있음
몽고DB에 데이터가 증가하여 더이상 하나의 Replica Set에 담을 수 없게 되면, 몽고DB는 데이터를 샤딩하여 분산시켜 줄 수 있음. 이러한 샤딩과정은 서비스 중단없이 온라인으로 진행된다. 만약 특정 샤드에 데이터가 몰리면 다른 샤드로 데이터를 이동시켜, 전반적으로 모든 샤드가 균등하게 데이터를 저장할 수 있도록 해준다. 이러한 동작을 밸런싱이라고 한다.
1-4. 유연성
여러가지 형태의 데이터를 쉽게 저장할 수 있음.
서비스 요구사항에 맞춰 다양한 종류의 데이터가 추가되어도 스키마 변경 과정 없이 필요한 데이터를 바로 저장하고 읽을 수 있다.
예를들어 날짜를 저장하고 싶은데 테이블에 날짜 관련 컬럼이 없다면 따로 필드를 추가해야 하는 테이블이 조작이 필요 하지만, 몽고디비는 그냥 써주면 알아서 인식된다.
만약 핸드폰에 OS종류를 구분해야한다면, RDBMS에서 컬럼을 새로 추가해야 할 것이다. 그런데 RYAN이라는 사람이 또 휴대폰을 구매하면 어떻게 해야할까? 또 컬럼을 추가해야 할까?
RDBMS는 이러한 상황을 테이블을 분리하고 관계를 맺음으로서 해결한다.
하지만 몽고DB는 Schema 가 없으므로 데이터 변경에 유연하게 대처할 수 있다. 몽고DB는 JSON 기반에 Document 모델을 사용하는데, Document 는 다양한 형태의 데이터를 한번에 담을 수 있으므로 여러가지 정보를 한눈에 볼 수 있게 표현해준다. 또한 Application에서 다루는 오브젝트와 1:1로 매칭되기때문에 개발자는 데이터를 쉽게 이해하고 빠르게 개발할 수 있다.
RDBMS는 복잡하게 테이블이 얽혀있는 것에 반해, NOSQL은 한눈에 보기 쉽도록 가독성이 높아진것을 확인 할 수 있다.
1-5. Index
- 다양한 조건으로 빠른 데이터 검색 (대부분 NoSQL들과 다른 몽고디비 만의 큰 차이점)
- 필요한 필드에 필요한 만큼 생성 가능
- 대용량 데이터에서 다양한 조건으로 조회 가능
- 다양한 형태의 Index 를 제공
Hashed Index (샤드 클러스터에서 데이터를 균등하게 분배하고자 할때 사용), Multikey Index, Partial Index, TTL Index (제한시간을 설정하여 오래된 데이터를 자동으로 지워주는 인덱스), Geospatial Index (공간인덱스) 공간내에 거리나 범위를 계산 카카오 택시, 카카오 대리, 카카오 모빌리티
2. MongoDB는 분산 시스템이 핵심이다
📍빅데이터 처리 특화
- Memory Mapped(데이터 쓰기 시에 OS의 가상 메모리에 데이터를 넣은 후 비동기로 디스크에 기록하는 방식)를 사용.
- 방대한 데이터를 빠르게 처리 가능
- 그러나 OS의 메모리를 활용하기 때문에 메모리가 차면 하드디스크로 데이터 처리하여 속도가 급격히 느려짐)
📍 NoSQL DB vs 관계형 DB 비교
항목 | NoSQL DB | 관계형 DB |
적합업무 | - 오프라인에서 정형 및 비정형 데이터 분석 업무 - 초당 동시 처리가 중요한 업무 - 로그 및 이력 등의 단순 기록형 업무 |
- 데이터 무결성 및 일관성이 중요한 트랜잭션 업무 - 온라인에서 다양한 집계 및 통계를 분석하는 업무 - 복잡한 계산 및 실시간 데이터 정합성이 필요한 업무 |
데이터 모델 | - 서비스에 맞는 DB 선택이 중요함 - 반정규화에 의한 설계를 기본으로 함 - 비정형화 스키마 구조로 미리 스키마를 선언하지 않음 |
- 엔티티 및 각 엔티티 간 관계를 정의함 - 엔티티 정의 시 정규화에 의한 설계가 중요함 - 테이블, 칼럼 등 DB요소에 대한 스키마를 엄격히 관리함 |
성능 | - 클러스터 크기, 네트워크 및 애플리케이션에 의해 성능이 결정됨 | - 성능 향상을 위해서는 성능 최적화 작업이 필요함 |
인터페이스 | - 쿼리 외 다양한 API를 통한 데이터 저장 및 검색이 가능함 | - SQL을 통해서만 데이터 저장 및 검색이 가능함 |
장점 | - 쿼리 프로세싱이 단순화되어 대용량 데이터 처리 성능이 향상됨 | - 데이터 중복 배제로 데이터 이상 발생 및 용량 증가를 최소화함 |
단점 | - 데이터 중복에 의해 데이터 일관성이 저하되고 용량이 증가함 | - 조인이 복잡한 경우 쿼리 프로세싱도 복잡해져 성능이 저하됨 |
기술 요소 | Master 서버에 장애 발생 시에도 여러 Slave 서버로 인해 무중단 서비스가 가능함 | 트랜잭션은 최소의 업무단위로 트랜잭션에 포함된 연산(① ~ ④)은 모두 처리되거나 또는 미처리되어야 함 |
각각의 데이터가 도달한 시점에 데이터가 갱신됨 | 트랜잭션 성공 시 DB는 일관된 상태를 유지해야 함 |
|
- 복제 메커니즘에 의해 모든 서버에 데이터 복제가 동시에 실행될 수 없음 - 시스템 부하 및 네트워크 속도에 따라 서버에 복제하는 시간이 다를 수 있으나 최종적으로는 모든 서버에 데이터가 복제됨 |
- 격리성 실행 중인 트랜잭션의 중간에 다른 트랜잭션이 접근할 수 없음 - 영속성 트랜잭션 성공 시 그 결과는 장애 발생 여부와 관계없이 DB에 저장되어야 함 |
3. MongoDB vs MySQL
3-1. MongoDB와 MySQL 비교
- MongoDB와 MySQL에서 동일한 데이터를 가지고 CRUD를 수행할 때, 대부분의 결과가 MongoDB가 빠르게 나옴
- MongoDB의 경우, Single Node와 Multi Node 간에 성능 차이는 거의 없음 (Delete 연산을 제외하고 대부분 Multi Node가 근소하게 빠름)
- MongoDB Multi Node의 Insert 연산 중에 연산 실패가 일어나는 경우 발생
- 분산을 목적으로 한 DBMS 선택할 경우, RDBMS에 비해 낮은 비용과 빠른 성능을 제공하는 MongoDB가 유리함
3-2. MongoDB와 MySQL 용어 비교
📍 3-2. Redis와 MongoDB의 차이점
(1) MongoDB: document 형식으로 데이터를 저장 Redis는 Key-value 형식으로 데이터를 저장
(2) Redis는 인메모리DB로 데이터를 메모리에 저장하고 관리하기 때문에 성능이 좋지만, 데이터를 유한하게 저장하기 때문에, 캐시등과 같이 데이터의 저장기한이 있고, 빠른 성능이 필요한 기능에 사용. MongoDB는 mysql처럼 서버-클라이언트 방식으로 설치해서 사용.(물리디스크) 메인 저장소나 로그 목적의 용도로 가변적 데이터 구조를 다루는데 유용함.
4. 간단한 mongoDB 사용
4-1. mongoDB 접속
본인 로컬 환경에 기본 설정으로 설치한 경우 아래 명령어로 접속한다.
mongosh
아래 명령어로 접속할수도 있다.
mongosh "mongodb://$host:$port"
[접속 화면]
[접속 확인]
4-2. mongosh로 DB 생성
use 명령어로 이미 존재하는 DB에 접속하거나, 새 DB를 생성할 수 있다.
4-3. Collection 생성
원하는 Collection 이름에 데이터 하나를 입력하면 생성된다.
4-4. 모든 DB 리스트 가져오기
show dbs
use로 사용할 DB 선택, Collections 확인하기
4-5. document 확인하기
4-6. document 삽입 (데이터 업데이트)
정보 1개 수정
db.[collection_name].updateOne({조건},{수정사항})
오직 하나의 필드만 변경하고 나머지 값을 모두 유지할 경우 $set을 사용하면 된다.
4-7. 데이터 여러개 수정
여러개의 데이터를 한 번에 수정하기 전에 샘플 데이터를 만들어 둔다.
db.fruit.insertOne({ "name" : "banana", "color" : "y", "likepoint" : 5 })
db.fruit.insertOne({ "name" : "orange", "color" : "o", "likepoint" : 5 })
db.fruit.insertOne({ "name" : "grape", "color" : "p", "likepoint" : 1 })
우선 찾으려고 하는 데이터 수를 카운트해보자.
db.[collection_name].countDocuments({조건})
2개의 테이터를 수정해 보기
배열 형태의 값을 변경해보자.
4-8. 데이터 삭제
4-9. 모든 데이터 조회
4-10. 중복값 삽입
db.fruit.insert([{"_id":1, "val":1},{"_id":1,"val":2},{"_id":2,"val":2},{"_id":3,"val":3}])
objectID로 사용하는 _id 값을 중복해서 삽입하면 다음과 같이 에러가 난다.
확인:
조회해보면 처음 들어갔던 { _id: 1, val: 1 } 만 저장되고, 나머지는 저장되지 않은 것을 확인할 수 있다.
그럼 중복된 _id는 건너뛰고 데이터를 저장해보자. ordered 옵션을 넣으면 된다.
db.fruit.insert([{"_id":1, "val":1},{"_id":1,"val":2},{"_id":2,"val":2},{"_id":3,"val":3}],{"ordered":false})
확인:
이번에는 중복된 데이터를 제외한 값이 모두 들어갔다.
참고
https://www.mongodb.com/docs/manual/reference/method/db.collection.countDocuments/
https://tychejin.tistory.com/349
https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=268367408
댓글