들어가기 앞서
본 내용은 MongoDB in Action (몽고디비 인 액션) 2nd Edition 를 참고 하였으며, 그 이외에 참고한 내용에 대해서는 각 개별 링크를 추가 하였습니다.
MongoDB 쉘 사용하기
먼저 장에서 이야기한대로, MongoDB 는 자바 스크립트를 사용한 쉘로 데이터를 입력, 조회 한다.
mongoDB 가 설치된 서버에서 mongo 명령어를 통해 MongoDB 쉘로 진입 할 수 있다.
쉘 시작
root@5a039f45f3a7:/# mongo
MongoDB shell version v5.0.3
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("402481c8-3ee5-47b3-b841-2b66585d678a") }
MongoDB server version: 5.0.3
... SKIP ...
>
DATA 유형
MongoDB 는 다음과 같이 데이터를 관리 한다.
document (도큐먼트) : 도큐먼트는 MongoDB 에서 관리 하는 JSON 으로 이루어진 데이터 단위이다. RDB 에서 ROW에 해당한다.
collection (컬렉션) : 컬렉션은 도큐먼트들의 집합이다. RDB 에서의 TABLE 에 해당한다.
database (데이터베이스) : 데이터베이스는 컬렉션들의 집합이다. RDB의 database 에 해당한다. 데이터베이스는 mongo DB 가 database 를 관리 하는 단위로, 실제 mongoDB 의 물리적 파일 단위로 생성되어, 해당 데이터베이스의 모든 컬렉션이 이 파일에 기록된다.
쉘에서 특정 컬렉션에 데이터를 접근하기 위해서는 쉘상에서 데이터베이스를 먼저 지정 하여야 한다. Mysql 에서 use database 명령을 내리고 쿼리를 수행하는것과 동일하다.
show dbs 명령어는 현재 존재하는 database 리스트를 조회한다
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
> db
test
> use ssg
switched to db ssg
> db
ssg
show dbs 명령어후 admin, config, local 이렇게 3개의 database 가 조회 되었다.
db 명령어는 현재 어떤 db 영역을 지정했는지를 나타내고, use 명령어는 전환을 원하는 db 로의 변경을 할수 있다.
위 예제에서 첫번째 db 명령어 실행시 test 로 나타나는데, 이는 mongo 쉘에 진입한 뒤, database 를 지정하지 않으면 test 로 기본 적용된다.
여기서, use ssg 명령어를 이용해 존재 하지 않는 ssg 로 database 를 변환하면, 신기하게도 database 가 전환된다.
collection도 마찬가지 인데, 존재 하지 않는 영역으로 전환은 가능 하지만, 이때 실제 database 와 collection 은 생성되지 않으며, 실제 document 가 처음 생성되는 시점에 스토리지에 write 된다. 굉장히 유동적으로 생성 가능한 방법인데, 실수로 다른 database 를 생성하는 오류가 발생 할 수도 있는 문제점도 있다. 이런 문제점 방지를 위해 strict 모드를 제공하고 있어, 해당 설정으로 사고를 미연에 방지 할 수 있다.
Insert & Query
document 실제 저장한뒤 조회 해보자.
우선 저장을 위한 document 를 하나 가정하고, 이 document 를 item 이라는 collection 에 저장 하도록 하겠다.
{itemId:"1000000000001"}
insert
아래와 같이 insert 명령어를 호출 하면 WriteResult 와 같이 처리 결과가 return 된다.
> db.item.insert({itemId:"1000000000001"})
WriteResult({ "nInserted" : 1 })
db 는 현재 지정된 db 데이터베이스의 객체 표현형이며, db 뒤에 있는 item 은 collection 을 의미한다. 해당 db의 collection 은 .(점) 으로 지칭되며, db.item 으로 표현된다.
해당 컬렉션에 insert 하는 명령어를 붙이고, 그 뒤에 () 괄호로 insert 하고자 하는 document 를 parameter 로 넘긴다.
이 명령어 하나만 보면, 실제 javascript 명령어를 사용하는 방식 그대로의 형태를 취하고 있다.
find
이제 저장된 데이터를 조회 해보자. 테스트를 위해 1000000000002, 1000000000003 을 더 입력 해보았다.
> db.item.find()
{ "_id" : ObjectId("6154784ec7a9636ab334f845"), "itemId" : "1000000000001" }
{ "_id" : ObjectId("61547850c7a9636ab334f846"), "itemId" : "1000000000002" }
{ "_id" : ObjectId("61547851c7a9636ab334f847"), "itemId" : "1000000000003" }
이번에는 find() 명령어를 호출 하면, item 컬렉션 내에 있는 모든 document 를 조회 할 수 있다.
조회 결과를 보면, 입력하지 않은 _id 항목이 노출 된다. _id는 mongoDB 에서 document 의 PK 로 사용되는 항목으로, document insert 시 지정하지 않으면, mongoDB 가 임의로 ID를 생성해 키를 부여한다. 물론, 직접 지정도 가능하다.
ad-hoc 질의를 작성해보자.
현재 3개의 document 에서, 1000000000002 값을 가지는 document 를 조회 하기 위해서는 다음과 같이 조회 조건을 parameter 로 넘긴다.
> db.item.find({"itemId" : "1000000000002"})
{ "_id" : ObjectId("61547850c7a9636ab334f846"), "itemId" : "1000000000002" }
예제와 같이 항상 parameter 는 document 형식으로 넘겨야 한다. 예제는 하나의 key-value 에 맞는 document 를 조회 하기 위해 하나의 쌍만 넘겼지만, 두 조건에 부합 하는 데이터를 찾기 위해선 (and 조건), 두 값을 모두 포함한 document 를 작성해 parameter 로 넘겨준다.
> db.item.find({ "_id" : ObjectId("61547850c7a9636ab334f846"), "itemId" : "1000000000002" })
{ "_id" : ObjectId("61547850c7a9636ab334f846"), "itemId" : "1000000000002" }
find({$and: {key:value})
위 and 조건은 다음과 같이 명시적으로도 조회 가능하다.
> db.item.find({$and:
... [ {"_id": ObjectId("61547850c7a9636ab334f846")}
... , {"itemId": "1000000000002" }
... ]}
... )
{ "_id" : ObjectId("61547850c7a9636ab334f846"), "itemId" : "1000000000002" }
$and 연산자를 이용해, and 조건이 되는 각 key 들을 개별 {} 으로 묶에 and 조건에 포함 시킨다. 물론, 조회 결과는 동일하다.
find({$or: {key:value})
or 도 마찬가지이다. $or 연산자를 이용해 다음과 같이 itemId 를 여러개 지정하여 조회 한다.
> db.item.find({$or:
... [ {"itemId": "1000000000002" }
... , {"itemId": "1000000000003" }
... ]}
... )
{ "_id" : ObjectId("61547850c7a9636ab334f846"), "itemId" : "1000000000002" }
{ "_id" : ObjectId("61547851c7a9636ab334f847"), "itemId" : "1000000000003" }
Document Update
update 는 두가지 방식으로 가능하다.
document 를 직접 대체 하는 방식과 , $set을 이용한 특정 key 값 변경하는 방법이다.
update({key:value}, {$set : {key:value}})
먼저 특정 값만 추가 해보자.
itemId가 1000000000001 을 가진 document 에 itemNm key 로 test01 이라는 값을 추가한뒤 해당 값을 다시 조회하였다.
> db.item.update({"itemId" : "1000000000001"}, {$set: {"itemNm" : "test01"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.item.find({"itemId" : "1000000000001"})
{ "_id" : ObjectId("6154784ec7a9636ab334f845"), "itemId" : "1000000000001", "itemNm" : "test01" }
update 는 find 와 다르게 parameter 가 두개로 이루어진다. 첫번째 parameter 를 변경해야 할 데이터를 탐색하는 조건이며, 두번째 parameter 는 설정할 key-value 이다. 결과적으로 지정한 document에 itemNm 키에 test01 값이 추가 되었다.
update({key:value}, {key:value})
두번째 값을 $set 연산자 없으 호출하면 어떻게 될까?
다음과 같이 두번째 paramter 에 $set 연산자 없이 {"itemNm" : "test01"} 만 입력 하였다.
> db.item.update({"itemId" : "1000000000001"}, {"itemNm" : "test01"})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.item.find({"itemId" : "1000000000001"})
> db.item.find({ "_id" : ObjectId("6154784ec7a9636ab334f845")})
{ "_id" : ObjectId("6154784ec7a9636ab334f845"), "itemNm" : "test01" }
itemId 키로 조회 한 결과, 조회 결과가 없었으며, pk 인 _id 로 조회 하니 그때서야 조회가 가능했다.
결국, itemId 가 사라지고, itemNm 만 들어간, 두번째 parameter document 로 대체 되었다.
update 시 연산자를 사용하느냐 마느냐에 대한 판단을 정확히 해야 시스템을 운영하는데 오류를 줄일 수 있다.
update({key:value}, {$unset : {key:value}})
지정된 값을 지우는건 $unset 연산자를 사용한다.
좀전의 1000000000001 상품의 데이터를 원래대로 설정 후, 지정한 itemNm 값을 unset 하였다.
> db.item.update({"itemNm" : "test01"}, {$set: {"itemId" : "1000000000001"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.item.find({"itemId" : "1000000000001"})
{ "_id" : ObjectId("6154784ec7a9636ab334f845"), "itemNm" : "test01", "itemId" : "1000000000001" }
> db.item.update({"itemId" : "1000000000001"}, {$unset: {"itemNm" : 1} })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.item.find({"itemId" : "1000000000001"})
{ "_id" : ObjectId("6154784ec7a9636ab334f845"), "itemId" : "1000000000001" }
$unset 명령시, itemNm 항목의 값을 1로 지정한 이유는, 나중에 설명하겠지만, 그렇게 설정하겠다는 의미이다.
update({key:value}, {$set : {document}})
좀더 복잡한 데이터를 업데이트 해보자.
지금까지는 일반적인 1:1 속성값만 추가 했는데, 이번에는 속성에 Document 를 부여 하겠다.
#set document
> db.item.update({itemId : "1000000000001"}, {
$set: {
"mltgItemNm" : {
"kor" : "korean itemNm",
"eng" : "english itemNm"
},
tag :["child", "toy"]
}
}
)
> db.item.find({"itemId" : "1000000000001"})
{ "_id" : ObjectId("6154784ec7a9636ab334f845"),
"itemId" : "1000000000001",
"mltgItemNm" : {
"kor" : "korean itemNm",
"eng" : "english itemNm"
},
"tag" : [ "child", "toy" ]
}
$set 연산자로 하위에 document 를 지정하니, 하위 항목으로 데이터가 설정 되었다. 추가된 하위 document 의 속성은 key-vaue 값 뿐만 아니라 key-array 항목도 추가 되어 있다.
$addToSet, $push
이때, array 항목에 대해서 특정 값을 추가 할 수 있다.
#addToSet
> db.item.update({itemId : "1000000000001"},
{ $addToSet:
{"tag": "baby"
}
},
true ,
false
)
> db.item.find({"itemId" : "1000000000001"})
{ ..skip..
"tag" : [ "child", "toy", "baby" ]
..skip..
}
#push
> db.item.update({itemId : "1000000000001"},
{ $push:
{"tag": "baby"
}
},
true ,
false
)
> db.item.find({"itemId" : "1000000000001"})
{ ..skip..
"tag" : [ "child", "toy", "baby", "baby" ]
..skip..
}
addToSet 은 현재 있는 array 에 값을 중복을 제거하여 추가 하는 방식이이고,
push 는, 중복을 허용하여 추가 하는 방식이다.
이때, update 펑션의 세, 네번째 parameter로 true/false 값이 추가되었는데, 의미는 다음과 같다.
세번째 parameter 는 upsert 를 할것인지 말것인지 이다. true일 경우, 검색 조건에 의해 찾은 document가 존재하지 않으면, 검색 조건을 포함하여 신규 document 를 생성하여 데이터를 입력한다.
네번째 parameter 는 다중 업데이트를 할지 에 대한 값이다. true 일 경우, 검색 조건에 의해 찾은 모든 document 를 update 하여, false 일 경우 검색 조건에 의해 찾은 첫번째 1개의 document 만 update 한다.
pretty
지금까지, 본문의 예제들은 모두 document 가 보기 좋게 formatting 되어 보였을 것이다. 이건 작성자가 예제를 작성할때, 가능한 보기 좋게 하기 위해 임의로 편집했기 때문이다.
mongoDB는 명령어로 formatting 해서 노출 하게 하는 function 도 제공 하다. 바로 pretty 이다.
> db.item.find().pretty()
{
"_id" : ObjectId("6154784ec7a9636ab334f845"),
"itemId" : "1000000000001",
"mltgItemNm" : {
"kor" : "korean itemNm",
"eng" : "english itemNm"
},
"tag" : [
"child",
"toy",
"baby",
"baby"
]
}
{ "_id" : ObjectId("61547850c7a9636ab334f846"), "itemId" : "1000000000002" }
{ "_id" : ObjectId("61547851c7a9636ab334f847"), "itemId" : "1000000000003" }
{
"_id" : ObjectId("615a4b2b2ae00c7965f41127"),
"itemId" : "1000000000014",
"tag" : [
"baby"
]
}
pretty 로 조회된 조회 결과는 위와 같이 정렬된 모습으로 조회 결과를 볼 수 있다.
Delete
데이터를 삭제 하기 위해선 $unset 명령어도 있지만, 명시적인 function 도 제공한다.
remove({key:value})
remove 는 조회 조건으로 조회된 document 를 삭제 한다. 다음과 같이 key-value 로 건을 넘기면, 해당 조건에 해당하는 document 를 삭제 한다.
> db.item.remove({itemId : "1000000000001"})
WriteResult({ "nRemoved" : 1 })
> db.item.find({"itemId" : "1000000000001"})
>
물론, 조건 절을 넣치 않으면, 해당 컬렉션 내 모든 데이터가 한꺼번에 삭제 된다.
drop
drop 는, 지정한 컬렉션을 전부 삭제한다. 이때는 해당 컬렉션의 index 를 포함한 collection 이 모두 삭제가 되는 것이기 때문에 해당 function 을 사용 할 때는 유의 해야 한다.
> db.item.drop()
true
> show collections
>
etc
이 이외에도 help 명령어를 이용해, 간단한 function 리스트를 조회 할 수 있다.
범위 조회 연산자
우선, 범위 검색을 위한 기초 데이터를 생성하자. mongoDB 는 javascript 명령어를 사용하므로, javascript 로 원하는데이터를 원하는 만큼 만들어 낼 수 있다.
우선 간단히 1000000 개의 itemId 와 itemNm 을 생성 하였다.
> for(i = 0; i < 100000; i++) {
... db.item.save({itemId: i, itemNm : "itemNm " + i});
... }
WriteResult({ "nInserted" : 1 })
> db.item.find();
{ "_id" : ObjectId("615a4f32a6f5523ff3739191"), "itemId" : 0, "itemNm" : "itemNm 0" }
{ "_id" : ObjectId("615a4f32a6f5523ff3739192"), "itemId" : 1, "itemNm" : "itemNm 1" }
{ "_id" : ObjectId("615a4f32a6f5523ff3739193"), "itemId" : 2, "itemNm" : "itemNm 2" }
{ "_id" : ObjectId("615a4f32a6f5523ff3739194"), "itemId" : 3, "itemNm" : "itemNm 3" }
{ "_id" : ObjectId("615a4f32a6f5523ff3739195"), "itemId" : 4, "itemNm" : "itemNm 4" }
{ "_id" : ObjectId("615a4f32a6f5523ff3739196"), "itemId" : 5, "itemNm" : "itemNm 5" }
{ "_id" : ObjectId("615a4f32a6f5523ff3739197"), "itemId" : 6, "itemNm" : "itemNm 6" }
{ "_id" : ObjectId("615a4f32a6f5523ff3739198"), "itemId" : 7, "itemNm" : "itemNm 7" }
{ "_id" : ObjectId("615a4f32a6f5523ff3739199"), "itemId" : 8, "itemNm" : "itemNm 8" }
{ "_id" : ObjectId("615a4f32a6f5523ff373919a"), "itemId" : 9, "itemNm" : "itemNm 9" }
{ "_id" : ObjectId("615a4f32a6f5523ff373919b"), "itemId" : 10, "itemNm" : "itemNm 10" }
{ "_id" : ObjectId("615a4f32a6f5523ff373919c"), "itemId" : 11, "itemNm" : "itemNm 11" }
{ "_id" : ObjectId("615a4f32a6f5523ff373919d"), "itemId" : 12, "itemNm" : "itemNm 12" }
{ "_id" : ObjectId("615a4f32a6f5523ff373919e"), "itemId" : 13, "itemNm" : "itemNm 13" }
{ "_id" : ObjectId("615a4f32a6f5523ff373919f"), "itemId" : 14, "itemNm" : "itemNm 14" }
{ "_id" : ObjectId("615a4f32a6f5523ff37391a0"), "itemId" : 15, "itemNm" : "itemNm 15" }
{ "_id" : ObjectId("615a4f32a6f5523ff37391a1"), "itemId" : 16, "itemNm" : "itemNm 16" }
{ "_id" : ObjectId("615a4f32a6f5523ff37391a2"), "itemId" : 17, "itemNm" : "itemNm 17" }
{ "_id" : ObjectId("615a4f32a6f5523ff37391a3"), "itemId" : 18, "itemNm" : "itemNm 18" }
{ "_id" : ObjectId("615a4f32a6f5523ff37391a4"), "itemId" : 19, "itemNm" : "itemNm 19" }
Type "it" for more
find() 명령어를 이용해 데이터롤 조회 하였더니, 우선 20개의 document 만 조회 되었다. 더 많은 row를 순차적으로 확인하려면 it 명령어를 이용 하면 된다.
$gt, $lt
단일 키를 조회 하기 위해서는 key-value 를 이용해 조회 하면 되지만, 특정 범위의 데이터를 조회 하려면 어떻게 해야 할까. $gt, $lt 연산자를 이용하면 된다. 다음은 10010 부터 10020 까지 9개의 데이터를 조회한 결과 이다.
> db.item.find({itemId:{"$gt":10010, "$lt":10020}})
{ "_id" : ObjectId("615a4f36a6f5523ff373b8ac"), "itemId" : 10011, "itemNm" : "itemNm 10011" }
{ "_id" : ObjectId("615a4f36a6f5523ff373b8ad"), "itemId" : 10012, "itemNm" : "itemNm 10012" }
{ "_id" : ObjectId("615a4f36a6f5523ff373b8ae"), "itemId" : 10013, "itemNm" : "itemNm 10013" }
{ "_id" : ObjectId("615a4f36a6f5523ff373b8af"), "itemId" : 10014, "itemNm" : "itemNm 10014" }
{ "_id" : ObjectId("615a4f36a6f5523ff373b8b0"), "itemId" : 10015, "itemNm" : "itemNm 10015" }
{ "_id" : ObjectId("615a4f36a6f5523ff373b8b1"), "itemId" : 10016, "itemNm" : "itemNm 10016" }
{ "_id" : ObjectId("615a4f36a6f5523ff373b8b2"), "itemId" : 10017, "itemNm" : "itemNm 10017" }
{ "_id" : ObjectId("615a4f36a6f5523ff373b8b3"), "itemId" : 10018, "itemNm" : "itemNm 10018" }
{ "_id" : ObjectId("615a4f36a6f5523ff373b8b4"), "itemId" : 10019, "itemNm" : "itemNm 10019" }
이외에 다음과 같은 연산자를 동일한 방법으로 사용할 수 있다.
$ne : not equal, 같지 않은.
$gte : grater than and equal, 크거나 같은
$lte : less than and equal, 작거나 같은
인덱스
많은 양의 데이터를 사용할 때 원하는 특정 데이터를 찾으려면 어쩔수 없이 모든 데이터를 탐색 해야한다. 이때, 인덱스가 필요하다. 인덱스는 특정 키를 이용해 미리 검색 알고리즘을 이용해 정렬한 다음, 해당 정렬된 포인터를 이용해 데이터를 찾아 결과값을 RETURN 한다. 해당 부분에 대해 알아보자.
explain()
우선, 조금 전에 사용한 범위 조건 연산자의 검색 정보를 조회 해보자. ORACLE 의 실행계획 (QUERY PLAN) 을 조회 하는 개념과 유사한 개념으로 다음과 같이 조회 한다. function 은 explain 을 사용했으며, parameter 로 executionStats 를 입력하여 상세 조회를 요청한다.
> db.item.explain("executionStats").find({itemId:{"$gt":10010, "$lt":10020}})
{
"explainVersion" : "1",
"queryPlanner" : {
"namespace" : "ssg.item",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"itemId" : {
"$lt" : 10020
}
},
{
"itemId" : {
"$gt" : 10010
}
}
]
},
"maxIndexedOrSolutionsReached" : false,
"maxIndexedAndSolutionsReached" : false,
"maxScansToExplodeReached" : false,
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"$and" : [
{
"itemId" : {
"$lt" : 10020
}
},
{
"itemId" : {
"$gt" : 10010
}
}
]
},
"direction" : "forward"
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 9,
"executionTimeMillis" : 56,
"totalKeysExamined" : 0,
"totalDocsExamined" : 100000,
"executionStages" : {
"stage" : "COLLSCAN",
"filter" : {
"$and" : [
{
"itemId" : {
"$lt" : 10020
}
},
{
"itemId" : {
"$gt" : 10010
}
}
]
},
"nReturned" : 9,
"executionTimeMillisEstimate" : 7,
"works" : 100002,
"advanced" : 9,
"needTime" : 99992,
"needYield" : 0,
"saveState" : 100,
"restoreState" : 100,
"isEOF" : 1,
"direction" : "forward",
"docsExamined" : 100000
}
},
... skip ...
"ok" : 1
}
>
인덱스가 없는 현재 구조에서, winningPlan 을 보면 statge 에 COLLSCAN 과 같이 컬럼 단위로 모두 다 조회 한뒤, 조회 조건에 해당하는 값을 찾는 것을 알 수 있다.
executionStats 를 보면, document 실행이 최종 100000 개 기준으로 처리 되었음 도 확인 할 수 있다.
조회 시간은 executionTimeMillis 를 보면 56ms 소요되었다.
createIndex({key:value})
이제, index 를 생성하고 생성한 인덱스를 확인해보자
> db.item.createIndex({"itemId":1})
{
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"createdCollectionAutomatically" : false,
"ok" : 1
}
> db.item.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_"
},
{
"v" : 2,
"key" : {
"itemId" : 1
},
"name" : "itemId_1"
}
]
우선 아무 조건없이 itemId 컬럼에 index 를 생성 하였다. "itemId" : 1 이면 오름차순 index이며, -1 이면 내림차순 인덱스 이다.
getIndexes() 로, 생성된 index 확인이 가능하다. 지금 막 생성한 인덱스는 1개 인데, 이미 하나가 더 존재 하고 있는것을 확인 할 수 있다. 이는 pk인 _id 의 pk index 인덱스로 기본적으로 오름차순으로 생성 된다. 놀라지 말자.
다시 아까 수행하였던 실행 계획을 다시 확인 해보자.
> db.item.explain("executionStats").find({itemId:{"$gt":10010, "$lt":10020}})
{
"explainVersion" : "1",
"queryPlanner" : {
... skip ...
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"itemId" : 1
},
"indexName" : "itemId_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"itemId" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"itemId" : [
"(10010.0, 10020.0)"
]
}
}
},
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 9,
"executionTimeMillis" : 1,
"totalKeysExamined" : 9,
"totalDocsExamined" : 9,
"executionStages" : {
... skip ...
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 9,
"executionTimeMillisEstimate" : 0,
"works" : 10,
"advanced" : 9,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"keyPattern" : {
"itemId" : 1
},
"indexName" : "itemId_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"itemId" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"itemId" : [
"(10010.0, 10020.0)"
]
},
"keysExamined" : 9,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0
}
}
},
... skip ...
"ok" : 1
}
>
인덱스 추가 후 SCAN 방식이 IXSCAN (인덱스 스캔) 으로 변경 되었으며, totalDocExmined 가 100000 개에서 9개로 줄었다. 결과적으로 executionTimeMillis 은 56ms 에서 1ms 로 줄어 들었다.
index 를 추가 하게 되면, 검색 속도는 빨라 지지만, 대신에 insert update 의 속도는 상대 적으로 느려질 수 밖에 없다. 때문에 적절한 상황을 고려하여 추가 하여야 한다.
DB Management
stats()
데이터 베이스를 운영하기 위해서는, DB의 스토리지 사용율, 현재 상태에 대해 확인 할 필요가 있다.
> show databases
admin 0.000GB
config 0.000GB
local 0.000GB
ssg 0.004GB
> db.stats()
{
"db" : "ssg",
"collections" : 1,
"views" : 0,
"objects" : 100000,
"avgObjSize" : 62.8889,
"dataSize" : 6288890,
"storageSize" : 1683456,
"freeStorageSize" : 20480,
"indexes" : 2,
"indexSize" : 2289664,
"indexFreeStorageSize" : 16384,
"totalSize" : 3973120,
"totalFreeStorageSize" : 36864,
"scaleFactor" : 1,
"fsUsedSize" : 10424041472,
"fsTotalSize" : 75125227520,
"ok" : 1
}
간단히 database 의 스토리지 사용율을 보려면 show databases 만으로도 확인 가능하다. 세부적인 내용은 stats() 를 이용해서 확인 가능하다. 컬렉션수, 스토리지 상세 사이즈 등 확인 가능하다.
각 컬렉션의 세부 정보는 컬렉션을 명시한 뒤 stats() 를 이용해 조회 한다.
> db.item.stats()
{
"ns" : "ssg.item",
"size" : 6288890,
"count" : 100000,
"avgObjSize" : 62,
"storageSize" : 1683456,
"freeStorageSize" : 20480,
"capped" : false,
... skip ...
"nindexes" : 2,
"indexBuilds" : [ ],
"totalIndexSize" : 2289664,
"totalSize" : 3973120,
"indexSizes" : {
"_id_" : 1085440,
"itemId_1" : 1204224
},
"scaleFactor" : 1,
"ok" : 1
}
컬렉션을 명시하면 컬렉션별 document 수와 사이즈를 확인 가능하다.
이상. 김지영.