DB(Database) & DBMS(Database Management System)
DB
- 전기적으로 저장된 데이터의 집합
- 영속성을 갖는 데이터 저장소
- 데이터를 각자가 정의한 구조로 저장
DBMS
- 데이터베이스를 운영하고 관리하는 소프트웨어(시스템). 전용 언어(SQL등)를 사용해서 관리하는 시스템.
- DB는 데이터를 체계적으로 보관하는 저장소의 개념이고, DBMS는 DB에 저장된 데이터에 체계적으로 접근하도록 하는 시스템.
- 크게 네 가지 기능
- 데이터 정의 : 저장하는 데이터의 구조 정의를 생성, 수정, 삭제한다.
- 데이터 업데이트 : 데이터 삽입, 수정, 삭제한다.
- 데이터 검색 : 저장된 데이터를 사용자가 원하는 형태로 가져온다.
- 관리 : DBMS 유저 등록/유저 모니터링/데이터 접근 권한 관리/성능 모니터링/무결성 유지/동시성 제어/정보 복구(특정 이벤트, 의도치않은 시스템 다운)
데이터 페이스 타입
1) 관계형 데이터베이스(Relational DBMS : RDB)
- 관계 대수를 기반으로 구현된 데이터베이스. 정형 데이터를 관리한다.
- 스프레드시트 형태의 데이터 구조를 가지고 있다고 생각하면 이해하기 쉽다.
SQL로 데이터를 검색, 업데이트할 수 있다.
SQL(Structured Query Language)
- DBMS에 데이터를 검색/업데이트(DML : Data Manipulation Language), 일련의 데이터 구조를 가진 구조체(테이블)를 생성/수정/삭제(DDL : Data Definition Language), 유저 관리(DCL : Data Control Language), DB transaction 관리(TCL : Transaction Control Language)를 지원하는 프로그래밍언어이다.
- 사람이 읽었을 때 이해하기 쉽도록 문법이 구성 되어있다.
- 모든 RDBMS의 SQL는 기본적으로 유사한 문법을 가지고 있다. 다만 독자적으로 가지고 있는 문법들도 있다.
- SQL을 작성해서 DB에게 명령을 보내는 행위는 사람이 스프레드시트를 만들어서 값을 채워 넣고 수정하고 지우는 과정이랑 매우 유사하다
- 일반적으로 DB라고 하면 RDBMS를 일컫지만 점점 달라지고 있다.
- MySQL, Oracle DB, MariaDB, PostgreSQL, SQLite
2) NoSQL 데이터베이스(NoSQL DBMS)
- 관계형 데이터베이스 이외의 데이터베이스(관계 대수를 엄격하게 따르지 않는다는 것)
- 제품마다 정형/비정형 데이터를 다룬다. 저장하는 데이터 구조가 다양하다.
- 구조 유형 : Document, key value, Object, Wide Column, Graph
→ MongoDB(Document), Redis(key-value), Cassandra(Wide column), Elasticsearch(Document), Neo4j(Graph)
MongoDB
문서 기반 DB(Document oriented DB)
- Mongo는 Humongous 에서 따온 말로, 엄청나게 큰 DB 라는 의미 → 대용량 데이터를 처리하기 좋게 만들어졌다.
- JSON like 문서(BSON 타입)를 지원하며 이러한 이유로 JSON형태의 데이터를 다루는 웹 생태계에서 큰 인기를 얻게 되었다.
- RDMS대비 비교적 단순한 설계(데이터 저장 방식)를 가지고 있기 때문에 진입 장벽이 낮은 장점이 있다.
- 간단한 CRUD기반의 서비스에 매우 적합하다.
- Aggregation Pipeline 기능을 활용하여 DB단에서 데이터 연산을 수행시킬 수 있다. 어플리케이션 코드의 부담을 줄일 수 있다. DB단에 연산을 수행시키는 것은 RDMS도 procedure라는 것을 이용해서 가능하다. 하지만 MongoDB의 aggregation 기능이 상대적으로 단순하고 사용하기 용이하다.
- 다양한 데이터를 저장하고 다뤄야하는 도메인에서 많이 사용한다.
- 데이터 간의 관계가 중요한 서비스에는 적합하지 않다.
관계형 데이터베이스와의 유사 개념
- 여러 개의 데이터베이스를 관리하고 하나의 데이터베이스는 여러 개의 컬렉션을 가지고 있다.
➡ SQL에서의 database와 유사
- 컬렉션은 다큐먼트의 집합이다.
➡ RDBMS의 테이블과 유사
- 다큐먼트는 MongoDB의 데이터 저장 단위이다. 사용자가 저장하는 데이터는 모두 다큐먼트로 저장된다.
➡ RDBMS의 레코드(테이블의 한 row)와 유사
다큐먼트
- BSON(Binary JSON) 형식의 데이터 구조 : JSON보다 더 많은 데이터 타입을 지원한다.
- 자바스크립트 객체랑 비슷한 형태로 필드명과 필드값을 가진다.
- 최신 버전 기준 16개의 데이터 타입을 지원한다.
- Text - String
- Numeric - 32-bit integer, 64-bit integer, Double
- Date - Date, Timestamp
- Other - Object, Array, Binary data, ObjectId, Boolean, Null, Regular Expression, JavaScript, Min key, Max key
필드명 제한
- "_id"는 사용할 수 없다. Primary key(고유값)로 예약되어 있는 이름이다.
- 필드명은 null 캐릭터(UTF-8 인코딩의 \u0000)를 포함할 수 없다.
- "$", "."을 제한적인 환경에서 포함할 수 있다.
문서 사이즈 제한
_id 필드
- 모든 MongoDB에 저장된 document들은 _id 필드를 가지고 있다. 고유값으로 DB에서의 primary key 역할을 수행한다.
- 새로운 document를 MongoDB에 생성(삽입)할 때 _id필드를 빼고 하면 MongoDB 드라이버가 알아서 _id필드를 생성해준다.
문서 필드 순서
- JS의 객체와는 다르게 MongoDB의 document의 필드들은 순서가 보장된다.
- 순서가 보장되어 있는 만큼 문서에 대한 데이터 검색를 할 때는 주의해야한다.
- MongoDB의 정의에 따라 {a: 1, b: 1}와 {a: 1, b: 1}는 같지만 {a: 1, b: 1}와 {b: 1, a: 1}는 다르다.
- MongoDB에서 document를 찾을 때는 exact match와 같은 데이터 검색(query)은 지양하는게 좋다. 조건문 검색이 좋다.
- 항상 _id 필드는 첫번째로 배정된다.
다큐먼트 CRUD
1) CRUD - Create
db.users.insertOne ( <- collection
{ <- document
name : "Lee", <- field: value
age : 26,
status : "pending"
}
)
- 하나의 document : db.collection.insertOne({ 필드 : 값, 필드 : 값, ... })
- 여러 document : db.collection.insertMany([{...}, {...}, {...}])
2) CRUD - Read
db.users.find( <- collection
{ age : {$gt: 18} }, <- query criteria
{ name : 1, address : 1 } <- projection
).limit(5) <- cursor modifier
- db.collection.find() : 하나/다수의 ducument 찾기
- $in : [] 을 사용하여 다중 값으로 검색, Mongoose는 쿼리 값으로 배열이 주어지면 자동으로 $in 쿼리를 생성해 줌
- $or : [] 를 사용하여 다중 조건 검색
- $lt, $lte, $gt, $gte 를 사용하여 range query 작성 가능
3) CRUD - Update
db.users.updateMany( <- collection
{ age : {$lt: 18} }, <- update filter
{ $set : { status : "reject" } } <- update action
)
- db.collection.updateOne() : 하나의 document 수정(단, 조건에 해당되는 document가 많으면 가장 처음 발견되는 document를 업데이트한다)
- db.collection.updateMany(): 다수의 document 수정
4) CRUD - Delete
db.users.deleteMany( <- collection
{ status: "reject" } <- delete filter
)
- db.collection.deleteOne() : 하나의 document 삭제 (단, 조건에 해당되는 document가 많으면 가장 처음 발견되는 document를 삭제한다)
- db.collection.deleteMany(): 다수의 document 삭제
- db.collection.deleteMany({}) : 전체 삭제
Mongoose(ODM)
ODM(Object Data Mapping)
- Node.js 어플리케이션이 MongoDB에 데이터를 CRUD할 수 있도록 도와주는 외부 패키지(라이브러리)
- MongoDB의 기본 Node.js 드라이버는 연결상태를 관리하기 어려운데 Mongoose를 사용하면 간단하게 데이터베이스와의 연결상태를 관리.
- Mongoose의 장점은 데이터를 단순하게 CRUD하는 것이 아니라, 데이터 검증(validation) + document를 JS 객체 변환까지 해주는 것.
- MongoDB에서 가져온 document 데이터를 JS 객체화한 것을 모델이라고 함.
- 개발자는 이 모델을 가지고 데이터를 객체지향 방식으로 수정
- 크게 검증 파트와 CRUD + document를 JS 객체 변환 파트.
- 검증 파트는 Schema 모듈이 담당하고, CRUD + document를 JS 객체 변환은 Model 모듈이 담당.
- Populate : MongoDB는 기본적으로 Join을 제공하지 않음. Join과 유사한 기능을 사용하기 위해선 aggregate 라는 복잡한 쿼리를 해야 하지만, Mongoose는 populate를 사용하여 간단하게 구현할 수 있음
Mongoose ODM 사용 순서
- 스키마 정의
- 모델 만들기
- 데이터베이스 연결
- 모델 사용
스키마(Schema)
- 한 collection의 document의 구조를 명시화한 객체
- Mongoose는 어플리케이션이 MongoDB에 데이터를 CRUD를 할 때 이 객체를 가지고 데이터 검증(각 필드 별 타입 검증)을 수행.
- 추가적인 검증도 커스텀 함수를 추가해서 가능. ex) 나이의 범위, 이름의 글자수 등
- 일종의 데이터 체크리스트
- mongoose.Schema => 이 함수의 리턴값은 객체.
- MongoDB와 데이터를 어떤 틀에 맞춰서 주고 받을지 정의하는 것
- MongoDB는 RDB와 달리 한 collection에 저장된 document가 모두 같은 데이터 구조를 가져야하는 강제성을 갖고있지 않다.
모델(Model)
- 스키마 객체를 사용해서 MongoDB에 있는 document 데이터를 JS의 객체 형태로 나타낼 수 있게 해줌.
- MongoDB에 있는 모든 document들에 대한 CRUD도 책임.
- JS 코드 상에서 실질적으로 자주 다루는 것이 모델.
- mongoose.model => 이 함수의 리턴값은 class. 이 class로 새로운 document 객체를 생성해서 MongoDB에 저장할 수도 있고 class의 static 메소드를 사용해서 document CRUD를 할 수도 있음. CRUD의 리턴값은 이 클래스의 객체.
- JS객체 형식으로 MongoDB와 데이터를 주고 받는 것
const mongoose = require("mongoose");
await mongoose.connect("mongodb://<your mongodb url>");
const coffeeSchema = mongoose.Schema({
type: String,
orderedBy: String,
});
const Coffee = mongoose.model("Coffee", coffeeSchema);
const { Schema } = require('mongoose');
const PostSchema = new Schema({
title: String,
content: String,
}, {
timestamps: true,
});
module.exports = PostSchema;
const mongoose = require('mongoose');
const PostSchema = require('./schemas/board');
exports.Post = mongoose.model('Post', PostSchema);
const mongoose = require('mongoose');
const { Post } = require('./models');
mongoose.connect('mongodb://localhost:27017/myapp');
connection pooling
- 재사용할 수 있는 active connection들을 관리하는 개념
- connection : 클라이언트와 서버 간의 특정 프로토콜을 이용한 연결이다. 한쪽이 연결을 끊으면 바로 없어진다.
- 하나의 풀(pool)장에 여러 active connection(끊기지 않은)을 담아 놓아서 재사용하는 것을 목적으로 한다.
재사용 = 연결을 새로 맺 는 수고(시간 및 연산 비용)를 줄일 수 있음
- 유연한 connection 관리로 application 입장에서는 query들의 병목 현상을 줄이고(throughput ↑), DB 입장에서는 클라이언트가 connection을 남용하지 않기 때문에 connection 가용성을 확보할 수 있다.
- 설정한 pool 사이즈 안에서 클라이언트는 connection을 마음대로 생성하고 사용하고 삭제할 수 있다.
- mongoose의 pool 사이즈는 mongoose.connect 메소드의 minPoolSize와 maxPoolSize옵션으로 조절할 수 있으며 maxPoolSize의 default값은 100이다.
- 현재 사용중인 connection의 갯수가 가용 가능한 connection의 갯수랑 같을 때 새로운 쿼리가 실행되면 새로운 connection을 생성한다