Humongous DB
▪ Document DB : BSON(Binary JSON)
▪ Auto Sharding
▪ Replica Set
▪ Index : Geospatial(위치정보 처리 index), Hashed, Unique, Spars, Compound
- Embedded Document, Array 필드도 인덱싱 가능
- 구체(Sphere) 모델 적용한 위치 데이터 인덱싱 지원 → 지구는 둥그니까..
- Full Text Search Index(한글 형태소 분석x), elasticsearch 사용을 권장
- Index를 사용한 TTL Collection
▪ Map/Reduce, Aggregation 기능 내장
- 대부분의 NOSQL은 자체 집계 기능을 제공하지 않음
▪ GridFS : grid file system. 한개의 document를 여러개 document로 분리하여 streaming서비스 등 개발 가능
▪ Javascript Shell : 완벽한 js shell 지원
> var x=1;
> var y=2;
> x+y
3
> function add(x,y){return x+y;}
> add(5,6)
11
집합적 데이터 모델
만약, RDB라면 order, customers, order lines, credit cards와 같이 4개 이상의 table이 필요하며, join query가 필요.
반면 mongoDB는 1개 document에 array와 embedded document로 조합 가능
mongoDB shell commands
기본 shell 사용
▪ show dbs : 데이터베이스 목록을 보여줌
▪ show collections : 현재 선택된 데이터베이스내의 컬렉션 목록을 보여줌
▪ use <dbname> : 데이터베이스를 선택함. 존재하지 않은 데이터베이스도 선택이 가능하다. 존재하지 않는 데이터베이스를 선택하였을 경우 데이터베이스 관련 물리적인 파일은 데이터베이스내에 컬렉션이 생성될 때 할당된다.
▪ db.<collection>.find() : 조회기능. 여러건의 Document를 조회할 때 사용함. findOne()은 조건에 부합되는 첫번째 Document 한건만을 리턴함.
▪ db.<collection>.insert(doc) : Document를 추가함.
▪ db.<collection>.update(cond, doc, option) : 조건에 부합되는 Document를 찾아 업데이트함. option이 부여되지 않으면 조건에 부합되는 Document 들중 첫번째 한건만 수정함.
▪ db.createCollection(colName, option) : 컬렉션의 명시적 생성. 암시적으로도 생성 가능
▪ cls : 화면 지우기
Insert
> use test
switched to db test
> show collections
users
> db.users.insert({userid:"gdhong",name:"홍길동"})
WriteResult({ "nInserted" : 1 })
> db.users.find()
{ "_id" : ObjectId("5d36c316412126c94e66bdb4"), "userid" : "gdhong", "name" : "홍길동" }
▪ _id 필드 : 고유 키 역할 수행
- 명시적으로 입력하지 않으면 ObjectID 객체 값이 자동 부여
- 명시적으로 입력하면 입력한 값으로 부여됨. 유일성이 확보되지 않은 경우 입력 불가
- ObjectID
update(cond, doc, option)
▪ cond : 조회 조건
▪ doc : 수정하려는 document
▪ option : { multi: false/true, upsert : false/true }
- multi(default false) : 한꺼번에 여러개 document 업데이트하지 않는다. 첫번째꺼 하나만 업데이트한다. noSQL이 atomic transaction이라는 개념을 유지하기 위해 default는 false이다.
- upsert(defult false) : 수정할 대상이 없는 경우 insert동작.
update 주의사항
▪ 원자적 업데이트
▪ 조건에 부합되는 document 들중 첫번째 것만 업데이트
- 여러건의 document를 업데이트하려면 multi:true 옵션을 부여함.
- 조건에 부합되는 document가 없을 경우 insert하려면 upsert:true 옵션 부여
▪ 내부적으로 문서 전체 치환이 일어남.
- 특정 필드만 업데이트하려면 적절한 제한자 사용
▪ _id 필드 값은 변경 불가
update : 문서 전체 치환
▪ 제한자의 필요성!!
- 특정 필드만 업데이트 하기 위해서...
- $set, $inc, $addToSet, $push, $pop, $pull 등
▪ upsert와 $inc : collection은 자동으로 생성됨.
db.peoples.update({ _id:"gdhong" }, { name:"홍길동" }) // 모든 필드 치환
db.peoples.update({ _id:"gdhong" }, {$set: { name:"홍길동" }}) // 해당 필드만 update
배열과 내장 문서 업데이트
▪ Document 구조의 강점
> db.users.insert({
_id : "A001", name : "홍길동",
phones : { home : "02-2211-5678", office : "02-3429-1234" }, email : [ "gdhong@hotmail.com", "gdhong@gmail.com" ]
})
WriteResult({ "nInserted" : 1 })
> db.users.update({ _id:"A001" }, { $set : { "phones.home" : "02-7788-9900" } })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne({ _id:"A001" })
{
"_id" : "A001",
"name" : "홍길동",
"phones" : {
"home" : "02-7788-9900",
"office" : "02-3429-1234"
},
"email" : [
"gdhong@hotmail.com",
"gdhong@gmail.com"
]
}
> db.users.update({ _id:"A001" }, { $addToSet : { email : "gdhong7788@daum.net" } })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.users.findOne({ _id:"A001" })
{
"_id" : "A001",
"name" : "홍길동",
"phones" : {
"home" : "02-7788-9900",
"office" : "02-3429-1234"
},
"email" : [
"gdhong@hotmail.com",
"gdhong@gmail.com",
"gdhong7788@daum.net"
]
}
Multi Update : 다수 docuement 한번에 update
삭제
▪ db.<collection>. remove(cond) - 조건에 부합되는 데이터만 삭제
- 인덱스, 컬렉션은 그대로 유지
▪ db.<collection>.drop()
- 데이터 뿐만 아니라 컬렉션에 설정된 Secondary Index까지 삭제
▪ db.dropDatabase()
- 현재 데이터베이스 전체 삭제
조회
▪ db.<collection>.find(cond, projection)
- cond : 조회 조건
- projection : 조회하고자 하는 필드 지정
▪ 조건 제한자
- $lt, $lte, $gt,$gte
- $or
db.scores.find({ score : { $gte : 91 } }, { name:1, score:1, _id: 0 })
db.scores.find({ score : { $gte : 80 } }) db.scores.find({ score : { $lte : 71, $gte : 80 } })
query 주의사항
조회조건으로 null을 사용할 경우 필드가 존재하지 않는 document도 조회함
> db.scores.insert({_id:1, kor : 80 })
WriteResult({ "nInserted" : 1 })
> db.scores.insert({_id:2, kor : 90 })
WriteResult({ "nInserted" : 1 })
> db.scores.insert({_id:3, kor : null })
WriteResult({ "nInserted" : 1 })
> db.scores.insert({_id:4, eng : null })
WriteResult({ "nInserted" : 1 })
> db.scores.find({ kor : null })
{ "_id" : 3, "kor" : null }
{ "_id" : 4, "eng" : null }
> db.scores.find({ eng : null })
{ "_id" : 1, "kor" : 80 }
{ "_id" : 2, "kor" : 90 }
{ "_id" : 3, "kor" : null }
{ "_id" : 4, "eng" : null }
이 문제를 해결하기 위해 $exists 제한자를 사용하여 추가적인 조건을 부여하면 됨.
> db.scores.find({ eng : { $in : [ null ], $exists : true } });
{ "_id" : 4, "eng" : null }
like절이 없으므로 정규식(REGEXRegExr) query를 사용해야만 함
//도시 이름이 ABE로 시작하는 것들
db.zipcodes.find({ city : /^ABE/})
//도시 이름이 ABE로 시작하고 뒤에 5글자의 alphanumeric이 이어지는 경우만 조회
db.zipcodes.find({ city : /^ABE(\w{5})$/})
//도시 이름이 A또는 B로 시작하고 마지막이 C로 끝나는 것들
db.zipcodes.find({ city : /^(A|B)\w+C$/})
배열을 조회시, 포함여부에 대해 확인하는 구문과 분리하여 사용하여야 한다.
> db.food.insert({ _id : 01, fruit : ["apple","banana","peach"] })
WriteResult({ "nInserted" : 1 })
> db.food.insert({ _id : 02, fruit : ["apple","pineapple","orange"] })
WriteResult({ "nInserted" : 1 })
> db.food.insert({ _id : 03, fruit : ["apple","banana","strawberry"] })
WriteResult({ "nInserted" : 1 })
> db.food.insert({ _id : 04, fruit : ["apple","banana"] })
WriteResult({ "nInserted" : 1 })
// list에 포함되면 모두 조회됨.
> db.food.find({ fruit : "banana"})
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 3, "fruit" : [ "apple", "banana", "strawberry" ] }
{ "_id" : 4, "fruit" : [ "apple", "banana" ] }
embedded document 검색시 따옴표(.) 을 사용하여 부분 값 비교 수행
> db.persons.insert({ _id : 1,
... name : { first: "John", last : "Kennedy" }, age : 40 });
db.persons.insert({ _id : 2, name : { first: "Will", last : "Smith" }, age : 31 });
db.persons.insert({ _id : 3, name : { first: "Barack", last : "Obama" }, age : 60 });
db.persons.insert({ _id : 4,
name : { first: "John", middle : "F", last : "Kennedy" }, age : 60 });
WriteResult({ "nInserted" : 1 })
> db.persons.insert({ _id : 2, name : { first: "Will", last : "Smith" }, age : 31 });
WriteResult({ "nInserted" : 1 })
> db.persons.insert({ _id : 3, name : { first: "Barack", last : "Obama" }, age : 60 });
WriteResult({ "nInserted" : 1 })
> db.persons.insert({ _id : 4,
... name : { first: "John", middle : "F", last : "Kennedy" }, age : 60 });
WriteResult({ "nInserted" : 1 })
> db.persons.find({ name : { first : "John", last : "Kennedy" } });
{ "_id" : 1, "name" : { "first" : "John", "last" : "Kennedy" }, "age" : 40 }
> db.persons.find({ "name.first" : "John", "name.last" : "Kennedy" });
{ "_id" : 1, "name" : { "first" : "John", "last" : "Kennedy" }, "age" : 40 }
{ "_id" : 4, "name" : { "first" : "John", "middle" : "F", "last" : "Kennedy" }, "age" : 60 }
query 결과
NOSQL 데이터 모델링
▪ findOne() : document 리턴
▪ find() : cursor객체 리턴
var cursor = db.persons.find(); while (cursor.hasNext()) {
var doc = cursor.next();
print(doc.name.first + " " + doc.name.last); }
▪ cursor 객체의 메서드
- sort(), limit(), skip()
- count()
- hasNext(), next()
//scores 컬렉션에서 name,score 필드만 조회하여 성적순으로 내림차순 정렬한 뒤
//앞에서 5개를 skip하고 10개만 조회함.
db.scores.find({ }, { name : 1, score : 1 }).sort({ score : -1 }).skip(5).limit(10);
Index 설정
- 설정방법은 아래 코드와 같음.
- embedded document 내부 value도 index설정이 가능.
// name 순 오름 차순 정렬된 인덱스 생성
db.scores.ensureIndex({ name : 1 });
// 복합 인덱스
db.scores.ensureIndex({ name : 1, scores : 1});
// 내장 문서 인덱스
db.persons.ensureIndex({ "name.last" : 1 });
// 배열 필드 인덱스(Multikeys 인덱스)
db.food.ensureIndex({ fruit:1 })
특수 인덱스
- sparse index
- unique index
- geospatial index - text index
- hashed Index
Map/Reduce
▪ 항상 이전 단계의 결과물이 다음 단계의 입력이 됨.
▪ shard key로 집계된 key로 집계를 수행하면 훨씬 성능이 빠름.
- 분산 reduce를 위해서는 shard가 반드시 필요.
Example) 1억건의 data가 있으면 4개 node에 2천5백개가 분산되어 정리하면 훨씬 빠름
▪ Map 결과가 1건이면 shuffling, reduce를 실행하지 않음
▪ shuffling은 mongodb 내부적으로 자동 수행됨
▪ finalize는 선택적임. reduce를 한번더 가공해야한다면 사용.
1) map으로 단어를 자름 + 셔플링
2) Reduce(group화)
Map/Reduce 예제
요구사항 : words collection에서 sentence key의 field에 존재하는 단어의 개수를 map reduce로 count 조회
1) 데이터 입력
> db.words.insert({sentence : "peter Piper picked a peck of pickled peppers"})
WriteResult({ "nInserted" : 1 })
> db.words.insert({sentence : "A peck of pickled peppers Peter Piper picked"})
WriteResult({ "nInserted" : 1 })
> db.words.insert({sentence : "If Peter Piper picked a peck of Peppers"})
WriteResult({ "nInserted" : 1 })
> db.words.insert({sentence : "Where's the peck of pickled peppers Peter Piper picked"})
WriteResult({ "nInserted" : 1 })
2) map, reduce function 정의
var map1 = function(){
// 주의사항 : 좌측은 반드시 tab이 아닌 space
var arr = this.sentence.match(/\w+/g); // 영어로된 단어 추출
for(var i=0;i<arr.length;i++){
emit(arr[i].toLowerCase(),{count:1})
}
}
var reduce1 = function(key, values){ // values=[{count:1},...]
var result = {count:0}; // map의 values 형식과 동일하게 맞춰줘야함
for(var i=0;i<values.length; i++){
result.count += values[i].count;
}
return result;
}
3) mapReduce function 실행
> db.words.mapReduce(map1, reduce1, {
out : { replace : "mr" }
})
{
"result" : "mr",
"timeMillis" : 304,
"counts" : {
"input" : 4,
"emit" : 34,
"reduce" : 8,
"output" : 12
},
"ok" : 1
}
4) 결과물 조회 : mr collection 조회
> db.mr.find();
{ "_id" : "a", "value" : { "count" : 3 } }
{ "_id" : "if", "value" : { "count" : 1 } }
{ "_id" : "of", "value" : { "count" : 4 } }
{ "_id" : "peck", "value" : { "count" : 4 } }
{ "_id" : "peppers", "value" : { "count" : 4 } }
{ "_id" : "peter", "value" : { "count" : 4 } }
{ "_id" : "picked", "value" : { "count" : 4 } }
{ "_id" : "pickled", "value" : { "count" : 3 } }
{ "_id" : "piper", "value" : { "count" : 4 } }
{ "_id" : "s", "value" : { "count" : 1 } }
{ "_id" : "the", "value" : { "count" : 1 } }
{ "_id" : "where", "value" : { "count" : 1 } }
Aggregation Framework
▪ pipeline 순서대로 처리
- $project, $match, $limit, $skip, $unwind, $group, $sort
▪ 이전 pipeline의 처리 결과는 다음 pipeline의 입력물이 됨.
Aggregation 예제
요구사항 : nasdaq 데이터에서 평균 종가를 구해보자
1) 데이터 입력 : 외부 bson file을 mongodb에 import
in mongodb/4.0.2/bin
$ ./mongorestore -d nasdaq -c stock /Users/a1003855/Desktop/stocks.bson
2019-07-24T11:30:12.749+0900 checking for collection data in /Users/a1003855/Desktop/stocks.bson
2019-07-24T11:30:12.948+0900 restoring nasdaq.stock from /Users/a1003855/Desktop/stocks.bson
2019-07-24T11:30:15.645+0900 [###.....................] nasdaq.stock 105MB/715MB (14.6%)
2019-07-24T11:30:18.648+0900 [#######.................] nasdaq.stock 217MB/715MB (30.3%)
2019-07-24T11:30:21.647+0900 [###########.............] nasdaq.stock 332MB/715MB (46.4%)
2019-07-24T11:30:24.645+0900 [##############..........] nasdaq.stock 429MB/715MB (59.9%)
2019-07-24T11:30:27.645+0900 [#################.......] nasdaq.stock 536MB/715MB (75.0%)
2019-07-24T11:30:30.646+0900 [#####################...] nasdaq.stock 631MB/715MB (88.2%)
2019-07-24T11:30:33.013+0900 [########################] nasdaq.stock 715MB/715MB (100.0%)
2019-07-24T11:30:33.013+0900 no indexes to restore
2019-07-24T11:30:33.013+0900 finished restoring nasdaq.stock (4308303 documents)
2019-07-24T11:30:33.013+0900 done
2) import 파일 확인
> db.stock.findOne()
{
"_id" : ObjectId("4d094f58c96767d7a0099d4a"),
"exchange" : "NASDAQ",
"stock_symbol" : "AACC",
"date" : "2008-03-06",
"open" : 9.03,
"high" : 9.03,
"low" : 8.41,
"close" : 8.56,
"volume" : 353600,
"adj close" : 8.56
}
3) index설정 : aggregate시 빠르게 동작을 위함. 없어도 동작이 되긴 됨
> db.stocks.createIndex({stock_symbol:1})
{
"createdCollectionAutomatically" : true,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
4) 기본 aggregate 테스트 : 전체 document가 다 return됨.
> db.stock.aggregate([ {$match:{stock_symbol:/^G/}} ])
{ "_id" : ObjectId("4d094f7cc96767d7a027cc8a"), "exchange" : "NASDAQ", "stock_symbol" : "GABC", "date" : "2008-03-07", "open" : 12.94, "high" : 12.94, "low" : 12.94, "close" : 12.94, "volume" : 100, "adj close" : 12.94 }
{ "_id" : ObjectId("4d094f7cc96767d7a027cc8c"), "exchange" : "NASDAQ", "stock_symbol" : "GABC", "date" : "2008-03-05", "open" : 12.89, "high" : 12.89, "low" : 12.89, "close" : 12.89, "volume" : 0, "adj close" : 12.89 }
...
5) aggregate 파이프라인 코드 작성
> db.stock.aggregate([
// 첫번째 파이프라인
{$match:{stock_symbol:/^G/}},
// 두번째 파이프라인
// 원래 필드이름을 alias해서 간단한 이름으로 변경 가능
{$project:{ _id:0, s:"$stock_symbol", c:"$close" }},
// 세번째 파이프라인
{
$group:{
_id : "$s",
avgclose : {$avg : "$c"},
maxclose : {$max : "$s"}
}
}
])
{ "_id" : "GYRO", "avgclose" : 29.26192069392813, "maxclose" : "GYRO" }
{ "_id" : "GTXI", "avgclose" : 12.439757516973811, "maxclose" : "GTXI" }
{ "_id" : "GTOP", "avgclose" : 7.405551181102362, "maxclose" : "GTOP" }
{ "_id" : "GRIN", "avgclose" : 1.9769006211180125, "maxclose" : "GRIN" }
{ "_id" : "GSOL", "avgclose" : 11.176468151216985, "maxclose" : "GSOL" }
{ "_id" : "GSIG", "avgclose" : 10.095608017817371, "maxclose" : "GSIG" }
{ "_id" : "GUID", "avgclose" : 12.86569579288026, "maxclose" : "GUID" }
{ "_id" : "GSIC", "avgclose" : 12.884677023712182, "maxclose" : "GSIC" }
{ "_id" : "GLUU", "avgclose" : 8.947736625514404, "maxclose" : "GLUU" }
{ "_id" : "GSBC", "avgclose" : 25.978493150684933, "maxclose" : "GSBC" }
...
6) aggregate 파이프라인 코드 + 정렬
> db.stock.aggregate([
// 첫번째 파이프라인
{$match:{stock_symbol:/^G/}},
// 두번째 파이프라인
// 원래 필드이름을 alias해서 간단한 이름으로 변경 가능
{$project:{ _id:0, s:"$stock_symbol", c:"$close" }},
// 세번째 파이프라인
{
$group:{
_id : "$s",
avgclose : {$avg : "$c"},
maxclose : {$max : "$s"}
}
}
// 평균종가 내림차순으로 정렬(평균종가가 큰게 상위)
,{$sort:{avgclose:-1}}
])
// 구글 1위
{ "_id" : "GOOG", "avgclose" : 389.0802908277405, "maxclose" : "GOOG" }
{ "_id" : "GFIG", "avgclose" : 57.10188461538462, "maxclose" : "GFIG" }
{ "_id" : "GRMN", "avgclose" : 47.309299503585216, "maxclose" : "GRMN" }
{ "_id" : "GENZ", "avgclose" : 45.8726703539823, "maxclose" : "GENZ" }
7) aggregate 파이프라인 코드 + 정렬 + collection output
> db.stock.aggregate([
// 첫번째 파이프라인
{$match:{stock_symbol:/^G/}},
// 두번째 파이프라인
// 원래 필드이름을 alias해서 간단한 이름으로 변경 가능
{$project:{ _id:0, s:"$stock_symbol", c:"$close" }},
// 세번째 파이프라인
{
$group:{
_id : "$s",
avgclose : {$avg : "$c"},
maxclose : {$max : "$s"}
}
},
// 평균종가 내림차순으로 정렬(평균종가가 큰게 상위)
{$sort:{avgclose:-1}},
// collectionOut 설정
{$out: "stocks_g_avg_max_close"}
])
> db.stocks_g_avg_max_close.find({_id:"GOOG"}) // stocks_g_avg_max_close collection
{ "_id" : "GOOG", "avgclose" : 389.0802908277405, "maxclose" : "GOOG" }
Map/Reduce vs Aggregate
기능 | Map/reduce | Aggregate |
대상 데이터 | 비정형, 반정형, 정형 모두 가능 | 정형, 반정형만 처리 가능 |
작성방식 | JS 함수 코딩 | Stage(Pipeline) 과 Expression 조합 |
실행환경 | JS 런타임 | Native |
실행결과 | 1.Out Collection (집계결과를 다른 collection에 저장) 2.Document |
1.Cursor 2.Out Collection |
작업방식 | 주로 Batch Job | 실시간 처리, Batch Job 모두 처리 가능 |
분산집계 (shard 환경, multi node) |
지원 | 지원 |
속도 | 느림 | 몇몇 경우 제외하고 더 빠름 |
MongoDB Java Driver
기본적인 Java Driver
아주 불편함. 각 key의 value에 대한 type을 모두 지정해줘야함.
MongoClient mongo = new MongoClient("localhost:27017");
mongo.setWriteConcern(WriteConcern.SAFE);
MongoDatabase test = mongo.getDatabase("test");
MongoCollection<Document> persons = test.getCollection("persons");
//FindIterable<Document> cursor = persons.find(new Document("_id", "gdhong123"));
MongoCursor<Document> cursor = persons.find().iterator();
System.out.println("이름, 국어, 영어, 국사, 수학");
System.out.println("======================================");
while (cursor.hasNext()) {
Document doc = cursor.next();
String name = doc.getString("name");
Document scores = (Document)doc.get("scores");
int kor = scores.getInteger("kor");
int eng = scores.getInteger("eng");
int his = scores.getInteger("his");
int mat = scores.getInteger("mat");
System.out.println(name + ", " + kor + ", " + eng + ", " + his + ", " + mat);
}
mongo.close();
Spring mongo 혹은 Jongo를 사용하는 방법
java 객체 ↔ bson
Jongo : Query in java as in mongo shell
SHELL : db.friends.find({age: {$gt: 18}})
JAVA DRIVER : friends.find(new BasicDBObject("age",new BasicDBObject("$gt",18)))
JONGO : friends.find("{age: {$gt: 18}}").as(Friend.class)
Jongo 예제코드
DB db = new MongoClient("localhost:27017").getDB("test2");
Jongo jongo = new Jongo(db);
MongoCollection persons = jongo.getCollection("persons");
List<String> emails1 = new ArrayList<String>();
emails1.add("gdhong@gmail.com");
emails1.add("gdhong123@yahoo.com");
Person p1 = new Person("gdhong", "홍길동", new Score(100,70,60,90), emails1);
persons.insert(p1);
List<String> emails2 = new ArrayList<String>();
emails2.add("mrlee@hotmail.com");
emails2.add("mrlee121@gmail.com");
Person p2 = new Person("mrlee", "이몽룡", new Score(80,90,70,70), emails2);
persons.insert(p2);
System.out.println("test2 에 쓰기 완료!!");
복제와 샤딩
복제
▪ 전형적인 Master/Slave 구조
- Master : 쓰기/읽기
- Slave : 읽기
▪ 자동장애조치 가능 : Replica Set(Primary/Secondary)
- Heartbeat 기반의 장애 노드 탐지, Primary 선출 기능
샤딩
▪ 샤드키 직접 지정
- Cassandra, Hbase : Rowkey = 샤드키
- 특정 DB, Collection 만 샤딩하도록 지정
▪ 샤딩 설정 정보를 저장하는 Config DB를 별도로 둠
▪ mongos : 라우터 역할
- 자동 샤딩 : 밸런싱, 청크 분할
MongoDB 모델링 하기
→ https://blog.voidmainvoid.net/241
'빅데이터 > nosql' 카테고리의 다른 글
mongodb shell에서 서로다른 database의 데이터 비교하기 (0) | 2019.08.22 |
---|---|
mongodb shell에서 db 이름 명시하여 데이터 조회하기 (432) | 2019.08.22 |
NoSQL강의) mongoDB에서 data 모델링하는 방법. 예제포함. (371) | 2019.07.25 |
NoSQL강의) Document Database 개요 및 설명 (384) | 2019.07.23 |
NoSQL강의) DynamoDB 개요, 특징 및 설명 (401) | 2019.07.23 |
NoSQL강의) HBase 개요, 특징, client 설명 + Apache Phoenix (390) | 2019.07.23 |