SQL vs NoSQL

SQL vs NOSQL


`SQL(Structured Query Language)`과 `NoSQL(Not only SQL)`은 데이터베이스 시스템의 두 가지 주요한 유형입니다. 이들은 데이터를 저장하고 조회하기 위한 다른 접근 방식과 구조를 가지고 있습니다. 다음은 `SQL`과 `NoSQL`의 주요 차이점과 각각의 장단점에 대한 설명입니다.

 

 

  SQL (관계형 데이터베이스)
구조화된 데이터 SQL 데이터베이스는 테이블과 열(Column)로 구성된 구조화된 데이터를 사용합니다. 데이터는 정해진 스키마에 따라 정확한 형식으로 저장됩니다.
관계 SQL 데이터베이스는 관계(Relationship)를 통해 여러 테이블을 연결할 수 있습니다. 이를 통해 데이터 간의 관계를 정의하고 관계형 모델을 사용하여 데이터를 조작하고 질의할 수 있습니다.
ACID 트랜잭션 SQL 데이터베이스는 ACID(원자성, 일관성, 고립성, 지속성) 트랜잭션을 지원하여 데이터의 안정성과 일관성을 보장합니다.
스케일 아웃의 제한 대부분의 SQL 데이터베이스는 수직적 확장(Vertical Scaling)에 의존하여 단일 서버의 성능을 향상시킵니다. 따라서 대규모 데이터 처리나 고가용성 요구사항에 제한이 있을 수 있습니다.

 

 

  NoSQL (비관계형 데이터베이스)
비구조화된 데이터 NoSQL 데이터베이스는 스키마가 없거나 유연한 스키마를 가지고 있어 데이터를 비구조적으로 저장할 수 있습니다. 이는 데이터 모델의 유연성을 제공합니다.
확장성 NoSQL 데이터베이스는 수평적 확장(Horizontal Scaling)을 통해 여러 서버에 데이터를 분산시켜 처리할 수 있습니다. 따라서 대용량 데이터 처리와 고가용성 요구사항을 더 잘 처리할 수 있습니다.
다양한 모델 NoSQL 데이터베이스는 다양한 데이터 모델을 제공합니다. Key-Value, Document, Column-Family, Graph 등 다양한 형태의 데이터 모델을 선택할 수 있습니다.
유연성과 성능 NoSQL 데이터베이스는 특정 사용 사례에 맞는 유연성과 높은 처리량을 제공합니다. 특히 읽기와 쓰기의 성능이 중요한 경우에 뛰어난 성능을 보여줍니다.

 

MongoDB


`MongoDB`는 `NoSQL`(비관계형) 데이터베이스 시스템으로, 대용량의 비정형 데이터를 저장하고 처리하는 데 특화된 데이터베이스입니다. `MongoDB`는 문서 지향적인 데이터 모델을 사용하며, 유연하고 확장성이 높은 데이터 저장 및 검색 솔루션을 제공합니다.

 

`MongoDB`는 `JSON`과 유사한 `BSON(Binary JSON)` 형식으로 데이터를 저장합니다. 이는 `JSON` 형식의 데이터를 이진 형식으로 직렬화하여 저장하므로 데이터의 크기를 줄이고, 빠른 읽기와 쓰기 작업을 지원합니다. `MongoDB`의 문서 단위 데이터 모델은 복잡한 계층 구조를 갖는 데이터를 쉽게 표현할 수 있게 해줍니다.

 

`MongoDB`는 분산 아키텍처를 지원하여 데이터의 확장성을 보장합니다. 데이터베이스의 크기를 수평적으로 확장할 수 있어서 여러 서버에 데이터를 분산하여 처리할 수 있습니다. 이러한 확장성은 데이터베이스 성능을 향상시키고, 대규모 데이터 처리 요구에 유연하게 대응할 수 있도록 합니다.

 

Mongoose


몽고디비 자체를 사용을 할 수는 있지만 본연의 문법으로 최적을 성능을 내거나 사용에 어려움이 있습니다. 이러한 부분을 해결하기 위해 사용되는 것이 `Mongoose`(이하 몽구스) 모듈 입니다. 몽구스는 `ODM(Object Document Mapping)` 기능을 지원해주고 있습니다.

 

이러한 `ODM`을 통해 기존 몽구스에 없던 개념이 생기는데 그것이 바로 스키마(Schema) 이다. 위에서 보았듯이 몽고디비는 스키마는 존재하지 않고 `Collection`이라는 상위 개념안에 `Document`를 입력하는 구조를 가지고 있습니다. 이러한 유연함은 편의성을 제공하지만 때로는 타입오류를 불러 일으키기 쉽습니다.

 

그래서 몽구스는 기본적으로 타입이 같은가에 대한 검증 단계를 한번 거치는 필터링 단계를 거칩니다. 또한 관계형 데이터베이스에 존재하는 Join기능과 비슷한 기능인 `Populate`를 제공함으로써 이전보다 더 놓은 성능을 지원하고 있습니다.

 

Replica set


Zero Down Time (무중단 서비스)

`MongoDB`는 `Zero Downtime`을 지원하기 위해 여러 기능과 전략을 제공합니다. `Zero Downtime`은 데이터베이스 서버의 유지 보수, 업그레이드, 재시작 또는 스케일 아웃 등의 작업을 수행할 때 서비스 중단 없이 작업을 진행하는 것을 의미합니다. 사용자들은 데이터베이스 서비스가 지속적으로 이용 가능하며, 아무런 중단 없이 계속 데이터에 접근하고 변경할 수 있습니다.

 

`MongoDB`에서 `Zero Downtime`을 달성하는데 사용되는 주요 기능과 전략은 다음과 같습니다.

 

  • `Replica Set`(복제 집합): `Replica Set`은 `MongoDB`에서 고가용성과 장애 복구 기능을 제공하는 기능입니다. 여러 `MongoDB` 인스턴스를 구성하여 하나의 프라이머리(primary) 인스턴스와 여러 개의 세컨더리(secondary) 인스턴스로 구성합니다. 프라이머리 인스턴스는 쓰기 작업을 처리하고, 세컨더리 인스턴스는 데이터를 복제하여 읽기 작업을 처리합니다. `Replica Set`을 사용하면 프라이머리 인스턴스에 장애가 발생해도 세컨더리 인스턴스가 자동으로 승격되어 서비스 중단 없이 서비스를 지속할 수 있습니다.

 

  • `Sharding`(샤딩): `Sharding`은 `MongoDB`에서 대규모 데이터를 처리하기 위한 확장성을 제공하는 기능입니다. 샤딩을 통해 데이터를 여러 조각으로 나누고 분산 저장하므로 쿼리 성능과 처리량을 향상시킵니다. 샤딩된 클러스터에서는 데이터를 저장하는 인스턴스가 여러 대로 분산되어 작동하며, 인스턴스가 중단되더라도 다른 인스턴스들이 서비스를 지속할 수 있습니다.

 

  • `Rolling Upgrades`(롤링 업그레이드): `MongoDB`는 롤링 업그레이드를 지원하여 데이터베이스 서버를 업그레이드할 때 `Zero Downtime`을 보장합니다. 롤링 업그레이드는 `Replica Set`의 세컨더리 인스턴스를 업그레이드한 후 프라이머리 인스턴스로 승격하여 업그레이드를 진행합니다. 이후 나머지 세컨더리 인스턴스들도 업그레이드를 진행하면서 `Zero Downtime`을 유지합니다.

 

  • `Automation Tools`(자동화 도구): `MongoDB`는 자동화 도구를 제공하여 일부 유지 보수 작업을 자동으로 처리하도록 지원합니다. 예를 들어, `MongoDB Atlas`와 같은 서비스에서는 백업, 모니터링, 확장, 스케일 아웃 등의 작업을 자동으로 관리하여 `Zero Downtime`을 실현합니다.

 

MongoDB 데이터 내장 vs 관계


관계형

`RDBS`에서는 여러 테이블을 작성하고 해당 테이블에 존재하는 데이터를 가져오기 위해서 `JOIN`문을 사용하여 호출을 하고 있습니다. 하지만 `NoSQL`의 경우 관계성을 가지고 있지 않다라는 특성을 가지고 있습니다. 그렇다면 이러한 문제는 어떻게 처리를 하고 있을까요?

 

위에서 설명했듯이 이러한 문제점을 해결하기 위해서 `mongoose` 모듈을 사용하게 됩니다. `mongoose`에서 제공하는 `populate()` 라는 함수를 사용하여 어떻게 호출 할 수 있는지 보도록 하겠습니다.

 

Post.findById(post._id)
  .populate('author')
  .exec((err, post) => {
    if (err) {
      console.log(err);
    } else {
      console.log(post);
    }
  });

 

위의 코드는 어떠한 글에 대한 내용을 호출 할 때, `populate` 저자들의 내용을 함께 호출하고 있습니다. 이러한 방식을 사용할 때는 대체로 자주 수정이 되는 내용들을 불러 올 때 사용하시면 되겠습니다.

 

하지만 이러한 `populate`도 단점이 존재 하는데 그것은 바로 `populate` 로 호출된 데이터에서 다시 추가 데이터를 불러오는 즉, `JOIN`문과 같이 사용하게 되면 성능적인 부분에서 치명적이라는 것입니다. 그러므로 그러한 데이터 호출이 발생할 시에는 DB설계를 한번 더 검토해보는 것을 추천합니다.

 

내장형

`MongoDB` 데이터 내장은 관계형 데이터베이스와는 달리, 여러 테이블에 걸쳐 있는 데이터를 한 문서 내에 내장시켜 저장하는 방식입니다.

 

최근의 트렌드는 데이터 내장을 사용하는 것이며, 이는 데이터를 검색할 때 조인을 사용하지 않아도 되므로 성능이 더욱 향상됩니다. 또한, 데이터의 일관성을 유지하기 위해 여러 테이블 간의 관계를 유지하는 것보다 더욱 간편합니다.

 

해당 기능 사용에 앞서 검토 해봐야 하는 사항으로는 해당 데이터가 수정 되는것이 거의 없고 읽기 방식으로 자주 처리되는가를 알아야 하고, 데이터의 크기가 얼마나 되는지를 알아야 합니다.

 

그렇다면 이러한 내장 방식은 어떻게 이루어 지는지 코드로 확인해 보도록 하겠습니다. 아래와 같이 `Mongoose`로 `Comment Schema` 가 만들어져 있다고 봅시다. 코멘트라는 내용은 어떠한 게시글의 댓글로써 스키마를 작성하였습니다.

 

const CommentSchema = Schema(
   {
      content: {
         type: String,
         require: true,
      },
      userId: {
         type: ObjectId,
         ref: "user",
         require: true,
      },
      userFullName: {
         type: String,
         require: true,
      },
      blog: {
         type: ObjectId,
         ref: "blog",
         require: true,
      },
   },
   { timestamps: true }
);

 

해당 댓글은 글마다 호출이 되어져야 하는 부분으로 블로그 스키마에 직접 입력하는 방식 즉, 내장하는 방식으로 작성해보겠습니다.

const BlogSchema = new Schema(
   {
      title: {
         type: "String",
         required: true,
      },
      content: {
         type: "String",
         required: true,
      },
      islive: {
         type: "Boolean",
         required: true,
         default: false,
      },
      comments: [CommentSchema],
   },
   { timestamps: true }
);

가장 아래에 있는 코드를 보면 `comment`라는 `key`의 `value`를 `CommentSchema`를 가지는 배열을 입력하였습니다. 이렇게 해당 스키마를 직접 입력을 하게 되면 Blog마다 각 댓글이 저장 되는 방식으로 진행 할 수 있습니다.

 

그렇다면 여기서 하나 생기는 의문이 있는데요. 스키마 전체를 입력 한다면 불필요한 `key` 값들도 입력을 하게 되는데 이러한 것들은 어떻게 처리할 수 있는가 입니다. 이러한 문제점을 해결 하기 위해서는 스키마를 그대로 넣기 보다는 `user key` 처럼 필요한 `key`값만 입력이 되도록 다시 코드를 작성을 주셔야 합니다.

     user: {
         _id: {
            type: Types.ObjectId,
            required: true,
            ref: "user",
         },
         username: {
            type: String,
            required: true,
         },
         name: {
            first: { type: String, required: true },
            last: { type: String, required: true },
         },
      },

 

하지만, 데이터 내장을 남용하면 문서의 크기가 커져서 디스크 공간을 많이 차지하게 되며, 만약 내장된 데이터를 수정해야 하는 경우 전체 문서를 업데이트해야 하므로 성능 문제가 발생할 수 있습니다. 따라서, 데이터 내장을 사용할 때도 신중히 고려해야 합니다.

'Backend > DataBase' 카테고리의 다른 글

SQLD 최적화  (0) 2023.11.08
SQL 활용  (0) 2023.11.07
SQL 기본  (1) 2023.11.07
데이터 모델링과 성능  (1) 2023.11.02
데이터 모델의 이해  (0) 2023.10.29