avatar

NoSQL 데이터베이스 MongoDB 알아보기

2022. 07. 26.
9

MongoDB란?

MongoDB란 데이터를 저장하는 데이터베이스 시스템의 한 종류로, 전통적인 테이블-관계 기반의 RDBMS가 아닌 도큐먼트 지향 NoSQL 데이터베이스 시스템입니다.

image

MongoDB의 특징

  • 동적 스키마

MongoDB는 테이블 구조가 고정되어 있는 형태(정적 스키마)가 아니라 JSON 형태의 동적 스키마형 문서를 사용합니다. 몽고디비에서는 그것을 Binary JSON(JavaScript Object Notaion)이라고 부르며, 줄여서 BSON 이라고 부르기도 합니다.

  • 키-값의 집합 문서

앞서 작성한 특징과 비슷한데, 몽고 DB의 문서는 키-값의 집합으로 되어 있기 때문에 자바스크립트의 객체 코드와도 비슷한 형태로 되어 있습니다. 그래서 들어가는 데이터에 따라서 구조가 변경됩니다.

MongoDB의 장점

  • 퍼포먼스가 뛰어납니다.

    • 기본적으로 읽기 및 쓰기 성능이 뛰어나기 때문에 많은 트래픽을 감당할 때 사용해도 퍼포먼스가 뛰어납니다. 실제로 RDB보다 수십배는 빠른 성능을 발휘한다고 합니다.
  • 개발이 편리합니다.

    • JSON 형태로 저장이 가능하기 때문에 직관적입니다.

MongoDB의 단점

  • 조인이 없습니다.

    • 조인이 없기 때문에 데이터 구조화를 할 필요가 있습니다. (MongoDB 3.2 버전 부터 보조적인 JOIN 기능으로 $lookup을 지원하기는 합니다.) 외래키의 개념이 없으며 데이터 구조의 동적인 특성 때문에 몽고 DB의 데이터 모델링은 역정규화(Denormalization)로 흐르곤 합니다.
  • 메모리에 의존적입니다.

    • 데이터 갱신 시 바로 디스크에 쓰는 것이 아니라 논리적으로 메모리에 쓰고나서 일정 주기에 따라서 비동기식으로 쓰기 때문에 메모리에 의존적이며 때로는 데이터가 유실 될 가능성이 존재하기도 합니다. 메모리에 의존적이기 때문에 메모리 크기가 성능을 좌우합니다.

MongoDB는 언제 쓰는 것이 좋을까?

Humongous(거대한) Database를 줄인 MongoDB라는 이름에서도 알 수 있듯이 MongoDB는 방대한 데이터 읽기가 필요한 경우에 사용하기 적합합니다. 로그성 데이터를 저장하는 경우, null 필드가 많이 존재하는 경우, 압도적인 퍼포먼스가 필요한 경우 등에 사용하기 좋습니다.

✔️ RDB와 MongoDB 용어 비교

RDB와 MongoDB 용어를 비교하는 경우 아래와 같습니다.

RDBMongoDB
TableCollection
RowDocument
ColumnField
Primary KeyObject_Id Field
RelationshipEmbedded & Link

✔️ 연산자 정리

👉 비교연산자

operator설명
$eq같음 (==)
$gt초과 (>)
$gte이상 (≥)
$lt미만 (<)
$lte이하 (≤)
$ne같지 않음 (!=)
$in전달한 배열 요소 중 하나
$nin배열 요소에 없거나 필드가 존재하지 않을 때 조회

👉 논리연산자

operator설명
$or주어진 조건 중 하나라도 true인 경우 true 반환
$and모든 조건이 true 이면 true 반환
$not해당 조건이 false 이면 true 반환
$nor모든 조건이 false 이면 true 반환

✔️ 쿼리 정리

👉 documents 조회(find)

db.<collection 이름>.find()
입력 key&value 값을 가진 docs만 리턴
db.<collection 이름>.find({ "books" : "Gravity" })
views의 값이 30이하인 docs만 리턴
db.<collection 이름>.find({ "views" : {$lte:30} })
쿼리 결과에 보여줄 field지정
db.<collection 이름>.find({},{ "_id" : false, "title" : true })
comments의 name이라는 field의 value가 Charles인 docs만 출력
  • $elemMatch : embedded doct 배열 쿼리시 사용
db.<collection 이름>.find({ "comments" : {$elemMatch : { "name" : "Charles" }}})
보기좋게 정렬: pretty()
db.cities.find({ name: "Minneapolis" }).pretty();
오름차순 & 내림차순 정렬: sort()
  • name field를 내림차순으로 정렬하여 출력
db.<collection 이름>.find({},{ "name" : true }).sort({ "name" : -1 })
  • id field를 내림차순으로 정리하고, name field를 오름차순으로 정렬하여 출력
db.<collection 이름>.find({},{ "name" : true }).sort({ "_id" : -1 , "name" : 1 })
5개 docs만 출력
db.<collection 이름>.find().limit(5)
2개를 건너뛰고 그 다음 docs를 출력
db.<collection 이름>.find().skip(2)

👉 데이터 업데이트

INSERT 하기
db.people.insert([
  { name: "Abet", age: 19 },
  { name: "Betty", age: 20 },
  { name: "Charlie", age: 23, skills: ["mongodb", "nodejs"] },
  { name: "David", age: 23, score: 20 },
]);
특정 field 업데이트 하기: $set
db.people.update({ name: "Abet" }, { $set: { age: 20 } }) >
  WriteResult({ nMatched: 1, nUpserted: 0, nModified: 1 });
특정 field 제거하기: $unset
db.people.update({ name: "David" }, { $unset: { score: 1 } }) >
  WriteResult({ nMatched: 1, nUpserted: 0, nModified: 1 });
field가 있으면 수정, 없으면 새로 추가하기 : upsert:true
db.people.update({ name: "Elly" }, { name: "Elly", age: 17 }, { upsert: true });
WriteResult({
  nMatched: 0,
  nUpserted: 1,
  nModified: 0,
  _id: ObjectId("56c893ffc694e4e7c8594240"),
});
특정 조건에 맞는 field 한꺼번에 수정하기: multi:true
db.people.update(
  { age: { $lte: 20 } },
  { $set: { score: 10 } },
  { multi: true }
) > WriteResult({ nMatched: 3, nUpserted: 0, nModified: 0 });
skills 배열에 새로운 값 추가하기: $push
db.people.update({ name: "Charlie" }, { $push: { skills: "angularjs" } });
WriteResult({ nMatched: 1, nUpserted: 0, nModified: 1 });
skills 배열에 여러 값 추가하고 오름차순으로 정렬하기: $each, $sort
db.people.update(
  { name: "Charlie" },
  {
    $push: {
      skills: {
        $each: ["c++", "java"],
        $sort: 1,
      },
    },
  }
) > WriteResult({ nMatched: 1, nUpserted: 0, nModified: 1 });
skills 배열에 하나의 요소 제거: $pull
db.people.update({ name: "Charlie" }, { $pull: { skills: "mongodb" } });

👉 커스텀 인덱스 추가

이미 존재하는 collection 에 추가할 경우
db.products.createIndex(
  { item: 1, quantity: -1 },
  { name: "query for inventory" }
);
인덱스 종류 쿼리
db.cities.getIndexes()
[
  {
    "v" : 2,
    "key" : {
      "_id" : 1
    },
    "name" : "_id_",
    "ns" : "test.cities"
  },
  {
    "v" : 2,
    "key" : {
      "_id" : 1,
      "checkins" : -1
    },
    "name" : "_id_1_checkins_-1",
    "background" : true,
    "ns" : "test.cities"
  },
  {
    "v" : 2,
    "key" : {
      "geolocation" : "2dsphere",
      "_id" : 1,
      "checkins" : -1
    },
    "name" : "geolocation_2dsphere__id_1_checkins_-1",
    "ns" : "test.cities",
    "background" : true,
    "2dsphereIndexVersion" : 3
  }
  // ... 생략됨

new schema, field 레벨 에서 추가

var animalSchema = new Schema({
  name: String,
  type: String,
  tags: { type: [String], index: true }, // animal의 tag를 인덱스로 지정
});

new schema, compound index는 항상 schema 레벨에서 추가하기

animalSchema.index({ name: 1, type: -1 }); // schema level