웹 시스템 개발 #MongoDB

학교 공부를 복습할 겸 적는 것이기에 내용이 부족할 수 있습니다.

 

부족한 것은 상관 없으나, 잘못된 부분이 발견된다면 지적해주시면 감사하겠습니다.

 


Scaling

Vertical Scaling

  • 정의: 시스템의 성능을 향상시키기 위해 메모리, CPU, 디스크와 같은 하드웨어 자원을 증가시키는 방법입니다.

Horizontal Scaling

  • 정의: 시스템을 여러 부분으로 나누고, 각 부분을 서로 다른 서버나 시스템에서 처리하는 방법입니다. 이 방법은 시스템의 확장성을 높이고 부하를 분산시키는데 효과적입니다.

 

 

NoSQL vs RDBMS

데이터 저장소를 선택할 때, 주로 고려되는 두 가지 옵션은 NoSQL관계형 데이터베이스 관리 시스템(RDBMS)입니다. 

전통의 RDBMS

  • 데이터 무결성 지원 : RDBMS는 Atomicity(원자성), Consistency(일관성), Isolation(독립성), Durability(지속성)의 원칙을 지원합니다.
  • 데이터 중복성 없음: 데이터베이스 설계에서 정규화를 통해 데이터 중복을 최소화합니다.
  • Strict Schema and Join: 엄격한 스키마와 조인 기능을 제공합니다. 데이터는 정해진 구조에 맞추어 저장되며, 여러 테이블 간의 관계를 통해 복잡한 쿼리를 실행할 수 있습니다.
  • No Horizontal Scaling: 수평 확장이 제한적입니다. 일반적으로 수직 확장(서버의 성능 향상)에 의존합니다.
  • Complex Query 지원: 복잡한 쿼리를 지원하여, 다양한 데이터 분석 및 처리가 가능합니다.

NoSQL

  • Schemaless: 고정되거나 미리 정의된 스키마가 없습니다. 
  • Horizontal Scaling: 수평 확장이 가능합니다. 데이터베이스를 여러 서버에 분산시켜 시스템의 부하를 분산하고 확장성을 높일 수 있습니다.
  • 데이터 중복 가능성: 스키마가 없고, 데이터가 분산되어 있어 데이터 중복이 발생할 수 있습니다.
  • 확장성이 필요한 경우: 특히 대규모 데이터를 다루거나, 빠르게 성장하는 애플리케이션에 적합합니다.

선택 기준

  • 데이터의 구조: 구조화된 데이터에는 RDBMS가, 비구조화된 또는 반구조화된 데이터에는 NoSQL이 적합할 수 있습니다.
  • 확장성 요구: 대규모 분산 시스템이 필요한 경우 NoSQL이 유리합니다.
  • 쿼리의 복잡성: 복잡한 쿼리가 필요한 경우 RDBMS가 더 적합할 수 있습니다.
  • 데이터 무결성과 일관성: 높은 수준의 데이터 무결성과 일관성이 요구되는 경우 RDBMS를 고려해야 합니다.

 

Local DBMS vs. Cloud Database Services

데이터베이스 관리 시스템(DBMS)의 선택은 로컬 DBMS클라우드 데이터베이스 서비스 간의 중요한 결정을 포함합니다. 

Local DBMS

  • 예시: MySQL, Oracle (RAC: Real Application Clusters), CUBRID, PostgreSQL, MariaDB, Microsoft SQL Server, Tibero (Tmax) 등.
  • 특징:
    • 물리적 서버 필요: 자체 서버 또는 호스팅된 서버에서 직접 설치하고 관리합니다.
    • 전체 제어: 데이터베이스 환경의 모든 측면을 사용자가 제어합니다.
    • 유지보수 및 관리 필요: 하드웨어 유지보수, 소프트웨어 업데이트, 백업 등을 직접 관리해야 합니다.

Cloud Database Services

  • 예시:
    • In-memory DB: Redis
    • 문서 기반: MongoDB Atlas
    • 칼럼 기반: HBase (AWS, Google Cloud, Azure에서 제공)
    • Cassandra: AWS, Google Cloud, Azure에서 제공
    • Elastic Search: 분산 검색 및 데이터 분석 엔진
    • Firebase: 서버리스 실시간 데이터베이스
  • 특징:
    • 투명성: 클라이언트에게 특정 DB를 사용하는지 여부가 투명하게 처리되는 경우가 많습니다.
    • 유연성 및 확장성: 수요에 따라 리소스를 쉽게 확장하거나 축소할 수 있습니다.
    • 비용 효율성: 초기 하드웨어 투자 비용이 필요 없고, 사용한 만큼만 비용을 지불합니다.
    • 유지보수 최소화: 클라우드 제공업체가 서버 유지보수, 업데이트, 백업 등을 관리합니다.
    • 글로벌 액세스: 인터넷이 연결된 어디서나 데이터에 접근할 수 있습니다.

선택 기준

  • 보안 및 컴플라이언스 요구 사항: 특정 보안 요구 사항이나 규제 준수가 중요한 경우, 로컬 DBMS가 적합할 수 있습니다.
  • 확장성 및 유연성: 빠르게 확장해야 하는 경우나 비용을 절감하고 싶은 경우, 클라우드 데이터베이스 서비스가 유리합니다.
  • 기술적 전문성: 클라우드 서비스는 기술적 유지보수 요구 사항을 줄이지만, 로컬 DBMS는 더 많은 기술적 제어를 제공합니다.
  • 비용 구조: 예산 및 비용 관리 측면에서 클라우드 서비스는 더 유연한 비용 구조를 제공합니다.

 

CAP vs. ACID

데이터베이스 시스템, 특히 분산 데이터베이스 시스템을 이해하는 데 있어서 CAP 이론ACID 속성은 핵심적인 개념입니다. 

ACID

  • 정의: ACID는 데이터베이스 트랜잭션의 네 가지 기본 속성을 나타냅니다. 이는 데이터의 정확성과 신뢰성을 보장하기 위한 것입니다.
  • 구성 요소:
    • Atomicity (원자성): 트랜잭션이 완전히 실행되거나 전혀 실행되지 않아야 함을 의미합니다.
    • Consistency (일관성): 트랜잭션이 데이터베이스를 항상 일관된 상태로 유지해야 함을 의미합니다.
    • Isolation (독립성): 동시에 실행되는 트랜잭션이 서로 영향을 주지 않아야 함을 의미합니다.
    • Durability (지속성): 트랜잭션이 성공적으로 완료되면, 그 결과가 영구적으로 반영되어야 함을 의미합니다.

CAP

  • 정의: CAP 이론은 2000년 에릭 브루어(Eric Brewer)에 의해 제안되었으며, 분산 데이터베이스 시스템이 동시에 세 가지 속성을 모두 만족시킬 수 없다고 주장합니다.
  • 구성 요소:
    • Consistency (일관성): 모든 노드가 동일한 시간에 동일한 데이터를 볼 수 있음을 의미합니다.
    • Availability (가용성): 모든 요청에 대해 항상 응답을 제공할 수 있음을 의미합니다.
    • Partition Tolerance (분할 내성): 네트워크 분할이 발생해도 시스템이 계속 작동할 수 있음을 의미합니다.

차이점

  • 적용 범위: ACID는 주로 전통적인 관계형 데이터베이스 관리 시스템(RDBMS)에 적용되는 반면, CAP는 분산 데이터베이스 시스템에 대한 이론입니다.
  • 목적: ACID는 데이터의 무결성과 신뢰성을 보장하는 데 초점을 맞추고, CAP는 분산 시스템의 한계와 트레이드오프를 설명합니다.
  • 선택의 문제: CAP 이론에 따르면, 분산 시스템은 세 가지 속성 중 두 가지만 선택할 수 있습니다. 이는 설계 시 중요한 고려 사항이 됩니다.

 

문서 중심의 NoSQL 데이터베이스인 MongoDB

MongoDB에서는 데이터 저장 구조를 유연하게 설계하여 다양한 데이터 유형을 수용하고, 복잡한 데이터 구조도 효율적으로 저장하고 검색할 수 있습니다. 

데이터베이스 및 컬렉션

  • 데이터베이스: MongoDB에서 데이터베이스는 컬렉션을 위한 컨테이너입니다. 
  • 컬렉션: 컬렉션은 MongoDB 문서 그룹이며, 단일 데이터베이스 내에 존재합니다. 컬렉션은 스키마를 적용하지 않기 때문에 컬렉션 내의 문서가 서로 다른 필드를 가질 수 있음을 의미합니다.

문서 및 BSON

  • 문서: MongoDB의 데이터는 MongoDB의 데이터 기본 단위인 문서에 저장됩니다. 문서는 JSON 객체와 유사하지만 BSON이라는 형식으로 저장됩니다.
  • BSON(바이너리 JSON): BSON은 JSON과 유사한 문서를 바이너리로 인코딩한 직렬화입니다. BSON은 JSON에서 지원하지 않는 날짜 및 이진 데이터와 같은 추가 데이터 유형을 지원합니다.

필드 이름

  • _id 필드_id 필드는 MongoDB 문서에서 기본 키 역할을 하는 특수 필드입니다. 지정되지 않은 경우 각 문서에 자동으로 추가되며 컬렉션 전체에서 고유하며 _id 값은 변경할 수 없습니다. 
    • _id 필드 값의 예: _id: ObjectId("5099803df3f4948bd2f98391"). ObjectId는 일반적으로 MongoDB에서 자동으로 생성되는 12바이트 식별자입니다.
  • 필드 이름 제한 사항: MongoDB 문서 필드 이름은 null 문자를 포함할 수 없으며 몇 가지 다른 제한 사항이 있습니다. 

 

mongosh(MongoDB 셸)에서 실행되는 명령

 

Select: 데이터베이스

  • > use <db>: 이 명령은 현재 데이터베이스 컨텍스트를 <db>로 전환하는 데 사용됩니다. 데이터베이스가 존재하지 않으면 MongoDB는 데이터를 처음 저장할 때 이를 생성합니다.

Use: 데이터베이스

  • > use mongodb_test: 컨텍스트를 mongodb_test 데이터베이스로 전환합니다. 존재하지 않는 경우 첫 번째 문서가 이 데이터베이스 내의 컬렉션에 삽입될 때 생성됩니다.
  • > db: 이 명령은 현재 선택된 데이터베이스의 이름을 반환합니다. 이전 명령이 실행된 경우 이름은 mongodb_test여야 합니다.
  • > show dbs: MongoDB 서버의 모든 데이터베이스를 나열합니다. MongoDB는 최소한 하나의 컬렉션과 해당 컬렉션 내에 하나의 문서가 있는 데이터베이스만 나열합니다.

Create: 데이터베이스 및 컬렉션

  • > use myNewDB: 컨텍스트를 myNewDB라는 데이터베이스로 전환합니다. 이전과 마찬가지로 존재하지 않는 경우 첫 번째 문서 삽입 시 생성됩니다.
  • > db.myNewCollection.insertOne({ x: 1 }): 이 명령은 myNewCollection이라는 컬렉션에 새 문서 { x: 1 }를 삽입합니다. myNewCollection이 아직 존재하지 않으면 MongoDB는 이 명령을 실행하는 과정에서 이를 생성합니다.

 

코드예시

export async function insertDocuments(db) {
    // Get the documents collection
    const collection = db.collection('restaurants');
    // Insert some documents
    const result = await collection.insertMany([
        {
            name: 'Sun Bakery Trattoria',
            stars: 4,
            categories: ['Pizza', 'Pasta', 'Italian', 'Coffee', 'Sandwiches']
        },
        {
            name: 'Blue Bagels Grill',
            stars: 3,
            categories: ['Bagels', 'Cookies', 'Sandwiches']
        }
    ]);
    return result;
}

 

  1. 컬렉션에 액세스
    • const collection = db.collection('restaurants')는 연결된 데이터베이스 내의 restaurants 컬렉션에 대한 참조를 검색합니다.
  2. 삽입할 문서 준비
    • 'name', 'stars', 'categories'와 같은 필드가 포함된 레스토랑을 나타내는 문서 개체 배열이 생성됩니다.
  3. 컬렉션에 문서 삽입
    • const result = wait collection.insertMany([...])는 문서 배열을 restaurants 컬렉션에 비동기식으로 삽입합니다. await 키워드는 insertMany가 반환한 Promise가 해결될 때까지 기다리는 데 사용됩니다.
  4. 결과 반환
    • 결과 객체에는 삽입된 문서 수, 삽입된 문서의 ID 등 삽입 작업에 대한 정보가 포함됩니다.

 

CRUD

MongoDB의 CRUD(생성, 읽기, 업데이트, 삭제)는 데이터베이스의 데이터에 대해 수행할 수 있는 기본 작업입니다.

 

Create

  • insertOne: 컬렉션에 단일 문서를 추가합니다. 컬렉션이 존재하지 않으면 MongoDB가 컬렉션을 생성합니다.
    • db.<collection>.insertOne({<field>:<value>, ...})
  • insertMany: 단일 명령을 사용하여 여러 문서를 컬렉션에 삽입합니다.
    • db.<collection>.insertMany([{<document1>}, {<document2>}, ...])
// Insert a single document
db.users.insertOne({ name: "John", age: 30, status: "A" });

// Insert multiple documents
db.users.insertMany([
  { name: "Jane", age: 25, status: "A" },
  { name: "Doe", age: 28, status: "B" }
]);

 

Read

  • find: 컬렉션에서 문서를 검색합니다. 결과를 필터링하는 쿼리와 반환되어야 하는 필드를 지정하는 예측을 지정할 수 있습니다.
    • db.<collection>.find(query, projection)
// Get all documents in the users collection
db.users.find({});

// Get all documents with age greater than 25
db.users.find({ age: { $gt: 25 } });

 

Update

  • updateOne: 지정된 필터를 기반으로 컬렉션 내의 단일 문서를 업데이트합니다.
    • db.<collection>.updateOne(filter, update, options)
  • updateMany: 컬렉션 내 필터와 일치하는 모든 문서를 업데이트합니다.
    • db.<collection>.updateMany(filter, update, options)
  • replaceOne: 필터와 일치하는 컬렉션 내의 단일 문서를 새 문서로 바꿉니다.
    • db.<collection>.replaceOne(filter, update, options)
// Update the first document where name is "John"
db.users.updateOne({ name: "John" }, { $set: { status: "B" } });

// Increment the age by 1 for all users with status "A"
db.users.updateMany({ status: "A" }, { $inc: { age: 1 } });

 

Delete

  • deleteOne: 필터와 일치하는 컬렉션에서 단일 문서를 제거합니다.
    • db.<collection>.deleteOne(filter, options)
  • deleteMany: 컬렉션에서 필터와 일치하는 모든 문서를 제거합니다.
    • db.<collection>.deleteMany(filter, options)
// Delete a single document where name is "John"
db.users.deleteOne({ name: "John" });

// Delete all documents where status is "B"
db.users.deleteMany({ status: "B" });

 

 

 

기본 _id 인덱스

MongoDB에서 새 컬렉션이 생성되면 _id 필드에 고유 인덱스가 자동으로 생성됩니다. 이 인덱스는 컬렉션 내 문서 전체에서 '_id' 값의 고유성을 높여 모든 문서가 '_id'로 효율적으로 검색될 수 있도록 보장해줍니다.

 

쿼리가 인덱스를 사용하지 않는 경우 MongoDB는 컬렉션 스캔을 수행합니다. 즉, 컬렉션의 모든 문서를 스캔하여 쿼리와 일치하는 문서를 찾는데, 대용량 데이터의 경우 매우 비효율적일 수 있습니다.

 

전체 컬렉션 검색을 방지하려면 쿼리에 자주 사용되는 필드에 인덱스를 생성할 수 있습니다.

 

색인이 없는 query

db.users.find({ score: { "$lt": 30 } })

이 경우 score 필드에 인덱스가 없으면 MongoDB는 users 컬렉션의 모든 문서를 스캔하여 score 필드가 30 미만인 문서를 찾습니다.

 

색인 추가

'scroe' 필드에 색인을 생성하여 이 쿼리를 더욱 효율적으로 만들 수 있습니다.

예: db.users.createIndex({ score: 1 })

이렇게 하면 score 필드에 오름차순 인덱스가 생성됩니다. 이를 통해 MongoDB는 인덱스를 사용하여 score가 30 미만인 모든 사용자를 빠르게 찾을 수 있습니다.

 

Index가 있어야하는 이유

MongoDB에서 인덱스는 인덱스 데이터 구조를 유지하기 위해 추가 쓰기 및 저장 공간을 소비하면서 컬렉션에 대한 데이터 검색 작업 속도를 향상시키는 데이터 구조입니다. 인덱스는 MongoDB에서 효율적인 쿼리 실행을 지원합니다. 인덱스가 없으면 MongoDB는 컬렉션 검색을 수행해야 하기 때문입니다. 

 

 

Embedding

임베딩 모델에서는 관련 데이터가 단일 문서 내에 직접 저장됩니다. 이 접근 방식은 밀접하게 결합되어 있고 중첩된 데이터가 무한히 커지지 않는 데이터에 적합합니다.

book = {
  title: "MongoDB: The Definitive Guide",
  authors: ["Kristina Chodorow", "Mike Dirolf"],
  published_date: ISODate("2010-09-24"),
  pages: 216,
  language: "English",
  publisher: {
    name: "O’Reilly Media",
    founded: "1980",
    location: "CA"
  }
}

 

이 예에서는 'publisher' 정보가 'book' 문서에 직접 삽입됩니다. 출판사를 포함한 전체 도서 정보를 한 번에 검색해야 하는 쿼리에 편리합니다.

그러나 출판사 데이터가 변경되면 이를 참조하는 모든 도서 문서에서 업데이트해야 하므로 비효율적일 수 있습니다.

 

Linking

연결 모델에서는 문서를 정규화하고 관련 문서의 ID를 저장하여 관계를 표현합니다. 이는 관계형 데이터베이스에서 외래 키가 작동하는 방식과 유사합니다.

 

book = {
  title: "MongoDB: The Definitive Guide",
  authors: ["Kristina Chodorow", "Mike Dirolf"],
  published_date: ISODate("2010-09-24"),
  pages: 216,
  language: "English",
  publisher_id: "8798486734"
}

publisher = {
  _id: "8798486734",
  name: "O’Reilly Media",
  founded: "1980",
  location: "CA"
}

 

여기서 'book' 문서에는 'publisher' 문서를 참조하는 'publisher_id' 필드가 포함되어 있습니다.

이 모델은 출판사 정보가 한 곳에만 저장되므로 데이터 중복을 피하고 데이터 일관성을 유지하려는 경우에 유용합니다.

그러나 출판사와 함께 전체 도서 정보를 검색하려면 도서에 대한 쿼리와 출판사에 대한 쿼리 두 개(또는 집계 파이프라인에서 $lookup을 사용한 조인 유사 작업)가 필요합니다.