인턴을 했던 회사에 MySQL 또는 MongoDB를 사용하는 프로젝트들이 있었는데, 각각을 선택한 것에 대한 차이점을 알고 싶었다.
주요 내용은 NoSQL과 SQL의 차이이며, mongoDB만의 특징은 그냥 참고용으로 적었다.
binary + json을 의미한다. mongodb에서는 bson 형태로 데이터를 저장한다.
~강대명 멘토님 시간에 배웠는데 금방 까먹었다.~
샤딩은 프로그래밍의 복잡성이 증가시키기 때문에 최대한 피하는 방법을 우선 찾아봐야 된다.
없다의 no가 아니라 Not Only의 줄인말이었다.
즉, SQL 뿐만 아니라~ 라는 뜻으로 SQL을 사용하는 RDB가 아닌 DB를 의미한다.
RDB로는 MySQL, Oracle, PostgreSQL 등이 있고, NoSQL에는 mongoDB, Redis, Cassandra, HBase 등이 있다.
NoSQL의 가장 큰 특징은 수평 확장 가능한 분산 시스템, Schema-less, 완화된 ACID 이다.
특히 SNS와 같이 간단하고 엄청나게 많은 데이터를 처리하기 위해, 그리고 기존 RDB로는 표현하기 힘든 다양한 데이터를 처리하기 위해 생겨났다.
자세한 차이는 아래 표를 보면 알 수 있다.
RDBMS | NoSQL | |
---|---|---|
적합한 사용례 | 데이터 정합성이 보장되어야 하는 은행 시스템 | 낮은 지연 시간, 가용성이 중요한 SNS 시스템 |
데이터 모델 | 정규화와 참조 무결성이 보장된 스키마 | 스키마가 없는 자유로운 데이터 모델 |
트랜잭션 | 강력한 ACID 지원 | 완화된 ACID(BASE) |
확장 | 하드웨어 강화(scale up) | 수평 확장 가능한 분산 아키텍처(scale out) |
API | SQL 쿼리 | 객체 기반 API 제공 |
위에 표가 전부 맞는 말은 아니다.
마치 NoSQL은 ACID가 안되는 것처럼 적어놨지만, RDBMS에 비해 부족한 것 뿐이지 가능하다.
또한 RDBMS도 수평 확장이 불가능한 것처럼 적어놨지만 MySQL Replication 등을 사용하면 수평 확장이 가능하다.
위 세 가지 단어가 mongoDB를 나타내주는 가장 큰 특징이다.
데이터는 document(그냥 json이라고 봐도 무방) 기반으로 구성되어 있고, ACID(산) 대신 BASE를 택하여 성능과 가용성을 우선시하며, open source라 무료이다.
또한 BSON으로 데이터가 쌓이기 때문에 Array 데이터나 Nested한 데이터를 쉽게 넣을 수 있다.
RDBMS | mongoDB |
---|---|
Database | Database |
Tables | Collections |
Rows | Documents |
Columns | Fields |
위 데이터 구조에서 ObjectId
라는 생소한 타입을 볼 수 있다.
ObjectId
는 RDBMS의 Primary Key와 같이 고유한 키를 의미하는데 차이점은 Primary Key는 DBMS가 직접 부여한다면 ObjectId
는 클라이언트에서 생성한다는 점이다.
이는 MongoDB 클러스터에서 Sharding된 데이터를 빠르게 가져오기 위함인데 Router(mongos)는 ObjectId
를 보고 데이터가 존재하는 Shard에서 데이터를 요청할 수 있다.
의아하게도 MongoDB 서버에서 알아서 ObjectId
를 부여해서 저장해도 될 것 같은데 딱히 지원해주지 않는다.
참고로 ObjectId
를 넣지 않고 저장한다면 데이터가 그대로 저장된다.
BASE는 ACID와 대립되는 개념으로 다음 세 가지로 이루어진다.
간단하게 정리하자면 일관성을 어느 정도 포기하고 가용성을 우선시, 즉, 일단 요청이 발생하면 데이터가 일치하지 않더라도 보내주는 것을 우선시한다는 것이다.
NoSQL이 나온 이유 중 하나는 대규모 데이터를 처리하는데 RDBMS에서는 한계가 있었기 때문이다.
일관성과 무결성을 조금 포기하더라도 빠른 읽기 성능과 수평 확장이 수월한 DB가 필요했다.
어떤 분산 시스템도 Consistency(일관성), Availability(가용성), Partition tolerance(분할 내성)을 모두 만족할 수 없다는 이론이다.
cf) 사진 출처: https://kciter.so/
CAP 이론에서 P 즉, 노드끼리 통신이 원활하지 않는 경우는 100% 방지할 수 없다는 사실에서 출발하여, 네트워크 파티션 상황은 반드시 발생한다고 가정한다.
PACELC은 다음으로 이루어져 있다.
구분 | 구성 |
---|---|
Partition | Availability |
Partition | Consistency |
Else | Latency |
Else | Consistency |
위에 두 개는 Partition이 발생할 경우에는 어떤 것을 취할 것인지, 아래 두 개는 Partition이 발생하지 않을 경우(정상 상태)에는 어떤 것을 취할 것인지를 결정한다.
PA / EL이라면 네트워크 파티션 상황일 때 가용성을 더 우선시하고 평상시에도 지연 시간을 더 신경쓰므로(일관성을 신경쓰느라 지연 시간이 늦어질수록 가용성이 떨어진다) 가용성을 우선시한다는 뜻이 된다.
mongoDB는 PA / EC 시스템이므로 네트워크 파티션 상황일 때는 가용성을 더 우선시하고 평상시에는 일관성을 더 우선시한다.
아래부터는 그냥 참고용이다.
mongoDB는 document라는 방식을 사용하기 때문에 모델링을 해야 하며 이를 위해 패턴을 정리할 필요가 있다.
일반적인 tree를 생각하면 된다.
기본적인 tree를 그릴 때 항상 문제가 되는 것은 부모를 어떻게 찾냐는 것이다.
이러한 문제에 따라 reference하는 법을 다르게 해 tree를 짤 수 있다.
[
{ _id: "MongoDB", parent: "Databases" },
{ _id: "dbm", parent: "Databases" },
{ _id: "Databases", parent: "Programming" },
{ _id: "Languages", parent: "Programming" },
{ _id: "Programming", parent: "Books" },
{ _id: "Books", parent: null }
]
[
{ _id: "MongoDB", children: [] },
{ _id: "dbm", children: [] },
{ _id: "Databases", children: [ "MongoDB", "dbm" ] },
{ _id: "Languages", children: [] },
{ _id: "Programming", children: [ "Databases", "Languages" ] },
{ _id: "Books", children: [ "Programming" ] }
]
[
{ _id: "MongoDB", ancestors: [ "Books", "Programming", "Databases" ], parent: "Databases" },
{ _id: "dbm", ancestors: [ "Books", "Programming", "Databases" ], parent: "Databases" },
{ _id: "Databases", ancestors: [ "Books", "Programming" ], parent: "Programming" },
{ _id: "Languages", ancestors: [ "Books", "Programming" ], parent: "Programming" },
{ _id: "Programming", ancestors: [ "Books" ], parent: "Books" },
{ _id: "Books", ancestors: [ ], parent: null }
]
[
{ _id: "Books", path: null },
{ _id: "Programming", path: "Books" },
{ _id: "Databases", path: "Books,Programming" },
{ _id: "Languages", path: "Books,Programming" },
{ _id: "MongoDB", path: "Books,Programming,Databases" },
{ _id: "dbm", path: "Books,Programming,Databases" }
]
[
{ _id: "Books", parent: 0, left: 1, right: 12 },
{ _id: "Programming", parent: "Books", left: 2, right: 11 },
{ _id: "Languages", parent: "Programming", left: 3, right: 4 },
{ _id: "Databases", parent: "Programming", left: 5, right: 10 },
{ _id: "MongoDB", parent: "Databases", left: 6, right: 7 },
{ _id: "dbm", parent: "Databases", left: 8, right: 9 }
]
글이 너무 길기 때문에 자세한 것은 https://kciter.so/posts/about-mongodb#modeling-pattern 여기서 참조하면 된다.
RDBMS와 NoSQL은 나온 배경이 다르다.
NoSQL은 빅데이터와 클라우드 등이 대두되는 현재, 최대한 단순하면서 많은 데이터를 다루기 위해 나타났다.
반면 RDBMS는 기존 정적인 데이터를 처리하기 위해 사용되었으며, 복잡하면서 무결성이 중요한 데이터에 적합하다.
시퀄라이즈에 비해 공식 문서가 훨씬 잘 되어 있는 편이지만 혹시 모를 나중을 위해 모델 정의 관련된 코드만 정리한다.
~쿼리문은 공식 문서가 잘 되어 있으니 패스~
참고로 몽구스는 ObjectId
라고 document마다 고유한 값을 가진 값을 자동으로 만들어준다.
timestamp: true
로 설정하면 createdAt과 updatedAt 값이 자동으로 할당되며, 서버 시간에 맞춰서 반영된다.
import { model, Schema } from 'mongoose'
export interface someInterface {
id: string
name: string
property: number
key: string
}
const someSchema = new Schema<someInterface>(
{
id: {
type: String,
required: true,
},
name: {
type: String,
required: true,
},
property: {
type: Number,
required: true,
},
key: {
type: String,
required: true,
},
},
{ timestamps: true }
)
someSchema.index({ id: 1, name: 1, property: 1 })
const SomeModel = model<someInterface>('Some', someSchema)
export default SomeModel
https://kciter.so/posts/about-mongodb
https://nesoy.github.io/articles/2018-05/Database-Shard