도큐먼트 지향 데이터
1.스키마 설계 원리
- 데이터베이스 스키마 설계
- 데이터베이스 시스템의 기능과 데이터의 특성, 애플리케이션의 요구사항이 주어졌을 때 데이터에 대한 최적의 표현을 찾아내는 과정
- 최적의 스키마 설계는 사용하는 데이터베이스에 대한 깊은 지식과 애플리케이션의 요구사항에 대한 현명한 판단, 오랜 경험에서 나옴
- 좋은 설계는 애플리케이션 확장과 성능을 고려한 변경과 같은 실험과 반복으로부터 나옴
- RDBMS와 MongoDB
- RDBMS
- 정규화된 데이터 모델을 권장
- 질의성 보장 및 데이터 무결성에 도움
- 패턴이 확립되어 있어 일대다, 다대다 관계 등의 생성 방법이 기존 RDBS 사용자들에게 익숙함
- MongoDB
- 스키마 설계 규칙 없음
- RDBMS
- 데이터베이스 시스템 모델링 고려시 제기할 수 있는 질문
- 애플리케이션의 액세스 패턴은 무언인가?
- 읽기/쓰기 비율은 어떻게 되는가?
- 쿼리는 키를 찾는 정도로 쉬운가 아니면 복잡한가?
- 집계가 필요한가?
- 데이터는 얼마나 저장되나?
- 데이터베이스에는 어떤 기능이 있는가?
- 좋은 고유 식별자(unique id)와 프리이머리 키를 무엇으로 만드는가?
- 애플리케이션의 액세스 패턴은 무언인가?
2.전자상거래 데이터 모델 설계
2.1스키마 기본
- RDBMS
- 조인이 가능하기에 다중 테이블 스키마 구조
- MongoDB
- 컬렉션에 대한 스키마가 필요하지 않기에 어떠한 도큐먼트라도 받아들이는 구조
- 상품 샘플 도큐먼트
{
_id: ObjectId("4c4b1476238d3b4dd5003981"), # 1.고유 객체 ID
slug: "wheelbarrow-9092", # 2.고유 슬러그
sku: "9092",
name: "Extra Large Wheelbarrow",
description: "Heavy duty wheelbarrow...",
details: { # 3.중첩 도큐먼트
weight: 47,
weight_units: "lbs",
model_num: 4039283402,
manufacturer: "Acme",
color: "Green"
},
total_reviews: 4,
average_review: 4.5,
pricing: {
retail: 589700,
sale: 489700,
},
price_history: [ # 4.일대다 관계
{
retail: 529700,
sale: 429700,
start: new Date(2010, 4, 1),
end: new Date(2010, 4, 8)
},
{
retail: 529700,
sale: 529700,
start: new Date(2010, 4, 9),
end: new Date(2010, 4, 16)
}
],
primary_category: ObjectId("6a5b1476238d3b4dd5000048"),
category_ids: [ # 5.다대다 관계
ObjectId("6a5b1476238d3b4dd5000048"),
ObjectId("6a5b1476238d3b4dd5000049")
],
main_cat_id: ObjectId("6a5b1476238d3b4dd5000048"),
tags: ["tools", "gardening", "soil"]
}- 고유 객체 ID: MongoDB 객체 ID
- 고유 슬러그
- 사용자 친화적인 고유 주소(permalinks)
- 도큐먼트에 대한 URL을 생성할 때는 슬러그 필드를 만들 것을 권장
- 고유 인덱스를 생성하여 해당 필드의 값이 빠른 쿼리 접근성과 고유성을 보장하도록 함
- 중첩 도큐먼트
- 서브 도큐먼트로 이미 존재하는 도큐먼트 내부에 또 다른 것들을 찾게 해준다는 점에서 _id 필드와는 전혀 다름
- 일대다 관계
- 다른 컬렉션에 있는 도큐먼트와 관계 설정이 필요할 때, 다른 컬랙션의 값을 도큐먼트가 오직 하나의 주요 값을 가지는 관계
- 다대다 관계
- RDBMS
- 조인 테이블 이용하여 다대다 관계 표현
- MongoDB
- 조인을 지원하지 않지만 다대다 관계 표현을 위해서는 객체 ID 값을 가지는 필드 필요
- 집계 시에는 조인과 유사한 기능 제공($lookup)
- 관계 구조
- 카테고리 샘플 도큐먼트
- RDBMS
{
_id: ObjectId("6a5b1476238d3b4dd5000048"),
slug: "gardening-tools",
name: "Gardening Tools",
description: "Gardening gadgets galore!",
parent_id: ObjectId("55804822812cb336b78728f9"),
ancestors: [
{
name: "Home",
_id: ObjectId("558048f0812cb336b78728fa"),
slug: "home"
},
Listing 4.2 A category document
80 CHAPTER 4 Document-oriented data
{
name: "Outdoors",
_id: ObjectId("55804822812cb336b78728f9"),
slug: "outdoors"
}
]
}- 카테고리에 속해 있는 모든 상품에 대한 질의
- db.products.find({category_ids: ObjectId('6a5b1476238d3b4dd5000048')})
- 어떤 상품이 속해 있는 모든 카테고리를 알기 위해서는 $in 연산자 사용
- db.categories.find({_id: {$in: product['category_ids']}})2.2사용자와 주문
- 아이템, 가격, 배송 주소를 가지고 있는 전자상거래 샘플 도큐먼트
{
_id: ObjectId("6a5b1476238d3b4dd5000048"),
user_id: ObjectId("4c4b1476238d3b4dd5000001"),
state: "CART",
line_items: [
{
_id: ObjectId("4c4b1476238d3b4dd5003981"),
sku: "9092",
name: "Extra Large Wheelbarrow",
quantity: 1,
pricing: {
retail: 5897,
sale: 4897,
}
},
{
_id: ObjectId("4c4b1476238d3b4dd5003982"),
sku: "10027",
name: "Rubberized Work Glove, Black",
quantity: 2,
pricing: {
retail: 1499,
sale: 1299
}
}
],
shipping_address: {
street: "588 5th Street",
city: "Brooklyn",
state: "NY",
zip: 11215
},
sub_total: 6196
}- 주소와 지불 방식을 가지고 있는 사용자 샘플 도큐먼트
{
_id: ObjectId("4c4b1476238d3b4dd5000001"),
username: "kbanker",
email: "kylebanker@gmail.com",
first_name: "Kyle",
last_name: "Banker",
hashed_password: "bd1cfa194c3a603e7186780824b04419",
addresses: [
{
name: "home",
street: "588 5th Street",
city: "Brooklyn",
state: "NY",
zip: 11215
},
{
name: "work",
street: "1 E. 23rd Street",
city: "New York",
state: "NY",
zip: 10010
}
],
payment_methods: [
{
name: "VISA",
payment_token: "43f6ba1dfda6b8106dc7"
}
]
}2.3상품평
- 상품평을 나타내는 샘플 도큐먼트
{
_id: ObjectId("4c4b1476238d3b4dd5000041"),
product_id: ObjectId("4c4b1476238d3b4dd5003981"),
date: new Date(2010, 5, 7),
title: "Amazing",
text: "Has a squeaky wheel, but still a darn good wheelbarrow.",
rating: 4,
user_id: ObjectId("4c4b1476238d3b4dd5000042"),
username: "dgreenthumb",
helpful_votes: 3,
voter_ids: [
ObjectId("4c4b1476238d3b4dd5000033"),
ObjectId("7a4f0376238d3b4dd5000003"),
ObjectId("92c21476238d3b4dd5000032")
]
}3.실제적 세부사항: 데이터베이스, 컬렉션, 도큐먼트
3.1데이터베이스
- 컬랙션과 인덱스의 물리적 모음과 동시에 네임스페이스
- 데이터베이스 관리
- 데이터베이스 생성하는 별도의 다른 방법은 없음
- 데이터베이스 내의 컬렉션에 쓰기를 하면 자동으로 생성
# 생성 및 연결
connection = Mongo::Client.new( [ '127.0.0.1:27017' ], :database => 'garden' )
db = connection.database
# 데이터베이스 선택(데이터베이스 인스턴스만 생성된 상태)
use garden
products = db['products']
products.insert_one({:name => "Extra Large Wheelbarrow"})
# 위 명령어 실행 후 컬렉션에 도큐먼트가 최초 생성되면서 컬렉션 생성 후 데이터베이스 생성
# 컬렉션 내 모든 도큐먼트 삭제
products.find({}).delete_many
# 컬렉션 삭제
products.drop
# MongoDB 셸에서 데이터베이스 삭제
use garden
db.dropDatabase();- 데이터 파일과 할당
- 데이터베이스가 생성될 때 MongoDB는 몇 가지 데이터 파일을 디스크에 할당
- 저장 데이터: 모든 컬렉션, 인덱스, 데이터베이스에 대한 메타데이터
- 저장 위치: dbPath에서 지정한 디렉터리에 저장되며, 지정되지 않았다면 /data/db에 저장
- 파일명: 데이터베이스의 이름을 따라 정해짐
- garden -> garden.ns
- 사용하는 공간과 할당된 공간 확인 명령어
- 데이터베이스가 생성될 때 MongoDB는 몇 가지 데이터 파일을 디스크에 할당
> db.stats()
{
"db" : "garden",
"collections" : 3,
"objects" : 5,
"avgObjSize" : 49.6,
"dataSize" : 248, # BSON 객체의 실제 크기
"storageSize" : 12288, # 컬렉션이 증가 대비한 여분 공간 및 삭제되었지만 아직 할당 안된 공간 포함
"numExtents" : 3,
"indexes" : 1,
"indexSize" : 8176, # 인덱스 전체 크기
"fileSize" : 201326592, # 데이터베이스에 할당된 파일의 전체 크기
"nsSizeMB" : 16,
"dataFileVersion" : {
"major" : 4,
"minor" : 5
},
"ok" : 1
} 3.2컬렉션
- 구조적 또는 개념적으로 비슷한 도큐먼트를 담고 있는 컨테이너
- 컬렉션 관리
- 별도의 명령어 없이 도큐먼트를 네임스페이스에 삽입하는 것으로 컬렉션 생성
# 컬렉션 생성
db.createCollection("users")
# 컬렉션 크기 지정 생성
db.createCollection("users", {size: 20000})
# .을 사용한 가상 네임스페이스 생성(편의를 위해 . 추가.다른 컬렉션과 동일한 처리)
products.categories
products.images
products.reviews
# 컬렉션 이름 변경
db.products.renameCollection("store_products")- 캡드 컬렉션
- 높은 성능의 로깅 기능을 위해 설계됨
- 일반 컬렉션과 달리 고정된 크기를 갖는 차이점으로 더 이상 공간이 없게 되면 도큐먼트 삽입 시 가장 오래된 도큐먼트를 덮어씀
- 캡드 컬렉션에 대한 사용자 행위 로깅 시뮬레이션
require 'mongo'
VIEW_PRODUCT = 0 # 사용자 행위 상수
ADD_TO_CART = 1
CHECKOUT = 2
PURCHASE = 3
client = Mongo::Client.new([ '127.0.0.1:27017' ], :database => 'garden')
client[:user_actions].drop
actions = client[:user_actions, :capped => true, :size => 16384] # user_actions 컬렉션
actions.create
500.times do |n| # n을 반복자로 하여 500회 반복하여 샘플 도큐먼트 생성
doc = {
:username => "kbanker",
:action_code => rand(4), # 0부터 3까지를 포함하는 임의의 값, 즉 0<=x<=3
:time => Time.now.utc,
:n => n
}
actions.insert_one(doc)
end > use garden
> db.user_actions.count();
160 # 캡드 컬렉션이라 크기 제한으로 160개 밖에 도큐먼트가 존재하지 않음
db.user_actions.find().pretty();
{
"_id" : ObjectId("51d1c69878b10e1a0e000040"),
"username" : "kbanker",
"action_code" : 3,
"time" : ISODate("2013-07-01T18:12:40.443Z"),
"n" : 340
}
{
"_id" : ObjectId("51d1c69878b10e1a0e000041"),
"username" : "kbanker",
"action_code" : 2,
"time" : ISODate("2013-07-01T18:12:40.444Z"),
"n" : 341
}
{
"_id" : ObjectId("51d1c69878b10e1a0e000042"),
"username" : "kbanker",
"action_code" : 2,
"time" : ISODate("2013-07-01T18:12:40.445Z"),
"n" : 342
}
... 생략 ...# 크기 및 도큐먼트 최대 개수 지정 생성
> db.createCollection("users.actions",
{capped: true, size: 16384, max: 100})- TTL 컬렉션
- 특정 시간이 경과한 도큐먼트를 만료시킬 수 있는 기능을 제공하는 컬렉션
- 실제로는 특별한 인덱스를 사용하여 구현한 것
- TTL 인덱스 생성 및 삽입 예제
# review 도큐먼트 1시간 후 삭제 설정
> db.reviews.createIndex({time_field: 1}, {expireAfterSeconds: 3600})
# 도큐먼트 추가
> db.reviews.insert({
time_field: new Date(), # 타임스탬프 값 저장
... 생략 ...
})- 시스템 컬렉션
- MongoDB 내부에서 컬렉션을 부분적으로 사용
- system.namespaces
- 현재 사용 중인 데이터베이스에서 정의된 모든 네임스페이스 확인
> db.system.namespaces.find();
{ "name" : "garden.system.indexes" }
{ "name" : "garden.products.$_id_" }
{ "name" : "garden.products" }
{ "name" : "garden.user_actions.$_id_" }
{ "name" : "garden.user_actions", "options" : { "create" : "user_actions", "capped" : true, "size" : 1024 } }- system.indexes
- 각 인덱스에 대한 정보 확인> db.system.indexes.find();
{ "v" : 1, "key" : { "_id" : 1 }, "ns" : "garden.products", "name" : "_id_" }
{ "v" : 1, "key" : { "_id" : 1 }, "ns" : "garden.user_actions", "name" : "_id_" }
{ "v" : 1, "key" : { "time_field" : 1 }, "name" : "time_field_1", "ns" : "garden.reviews", "expireAfterSeconds" : 3600 }3.3도큐먼트와 인서트
- 도큐먼트 시리얼라이제이션, 타입 그리고 한계
- 드라이버는 모든 도큐먼트를 저장하기 전에 BSON으로 시리얼라이즈되고 나중에 BSON으로 디시리얼라이즈 처리함
'dev > DB' 카테고리의 다른 글
| MongoDB - 집계1 (0) | 2021.10.28 |
|---|---|
| MongoDB - Query Selectors(1) (0) | 2021.10.22 |
| 자바스크립트 셸을 통한 MongoDB (0) | 2021.09.30 |
| MongoDB 핵심 기능 요약 (0) | 2021.09.27 |
| MongoDB 설치 (0) | 2020.07.27 |