레이블이 MongoDB인 게시물을 표시합니다. 모든 게시물 표시
레이블이 MongoDB인 게시물을 표시합니다. 모든 게시물 표시

mongodb-go-driver cursor not found error

# mongo go client v1.9.1 (https://github.com/mongodb/mongo-go-driver) 로 documents 조회(find)를 다음과 같이 구현했다.
cursor, err := c.collection.Find(context.TODO(), filter)
if err != nil {
    return err
}
defer cursor.Close(context.TODO())
return cursor.All(context.TODO(),  results)

# 여러개의 조회 요청이 동시에 들어오면 일부 요청 결과에 다음 에러가 발생했다.
(CursorNotFound) Cursor not found (namespace:

# 에러를 발생하는 mongo-go-driver 부분
# https://github.com/mongodb/mongo-go-driver/blob/4f06ad2489b73cd1dbcebb5e7df09b77cb643be1/mongo/cursor.go#L266

# document 수가 50개로 적은 컬렉션에 대해서 조회시 발생하지 않고,
# document 수가 156개인 컬렉션에서 발생했다.
 
# 커서를 읽기(cursor.All)전에 현재 커서에 있는 document 를 찍어보면 101로 보인다.
cursor.RemainingBatchLength()

# 이유는 default batchSize=101 이기 때문이다.
# 인덱스 없는 정렬시에는 전체 document 를 로딩한다고 한다.
```
find() and aggregate() operations have an initial batch size of 101 documents by default. Subsequent getMore operations issued against the resulting cursor have no default batch size, so they are limited only by the 16 megabyte message size.

For queries that include a sort operation without an index, the server must load all the documents in memory to perform the sort before returning any results.
```

# Find 옵션으로 batch 크기를 다음과 같이 설정했다.
opts := options.Find()
opts = opts.SetBatchSize(500)
cursor, err := c.collection.Find(context.TODO(), filter, opts)

# cursor.RemainingBatchLength()를 출력해보면 156 이다.
# 위와 같이 한번에 전체 documents 를 가져오니 cursor not found 에러가 발생하지 않았다.
# batchSize 가 전체 documents 보다 작으면 계속 batchSize 만큼 조회해서 가져오는것으로 보인다.(실제 batchSize 101일때 1번 find 최종 조회 결과는 156개의데이터를 모두 가져왔다.)
# 하지만 이 과정에서 동시에 조회가 실행될 경우 서버로 부터 커서를 찾을 수 없다는 응답(헤더)를 받는것으로 보인다.

# 참고
# default batchSize 로 인한 지연
# batchSize 설정값에 따른 성능 결과

mongodb tools

# mongodb 백업 복구등에 사용하는 툴
# 참고로 mongo cli db.copyDatabase() 같은 함수는 4.0 부터 deprecated
# mac 환경에서 툴 설치
brew tap mongodb/brew
brew install mongodb-database-tools

# dump 후 dump 디렉토리에 db 디렉토리만 .bson 등의 파일이 생성된다.
mongodump --uri="mongodb://ysoftman:password123@10.10.10.1:27017/?authSource=admin" --db=my_db -c=my_collection -vvv

# localhost MongoDB 로 복구
mongorestore --uri="mongodb://localhost:27017/" -vvv

# my_db -> bill_lemon_db 로 이름 변경해 복구
mongorestore --uri="mongodb://localhost:27017" --db=bill_lemon_db ./dump/my_db -vvv

# 특정 db > 컬렉션만 export
mongoexport --uri="mongodb://ysoftman:password123@10.10.10.1:27017/?authSource=admin" --db=my_db -c=my_collection -o=out.json -vvv

# localhost MongoDB > ysoftmandb > clusters 만 import
# upsert 모드를 사용하면 매칭되는 doc 은 삭제하고 import 되는 doc 을 추가한다.
mongoimport --uri="mongodb://localhost:27017" --db=ysoftmandb -c=clusters --mode=upsert --file=out.json -vvv

mongodb find pagination

# mongodb collection 의 document 조회(find)시 pagination
    // document _id 다음 포맷으로 구성되어 있어, _id 크기비교로 범위를 탐색할 수 있다.
    // https://www.mongodb.com/docs/manual/reference/method/ObjectId/#objectid
    // A 4-byte timestamp + A 5-byte random value + A 3-byte incrementing counter
    // 가장 오래된 document 파악
    filter = bson.D{{Key: "number", Value: bson.D{{Key: "$gt", Value: 0}}}}
    docs = make([]samepleDoc1, 0)
    // 가장 오래된 document 부터 2개 문서 가져오기
    c.FindByPageSize(filter, &docs, 2)
    page := 0
    lastObjID := docs[len(docs)-1].ObjId
    PrintDoc(docs)
    page++
    fmt.Printf("lastObjID %v page %v\n", lastObjID, page)
    // 페이지당 2개 documents 로 계속 조회
    for len(docs) > 0 {
        lastObjID := docs[len(docs)-1].ObjId
        filter = bson.D{{Key: "_id", Value: bson.D{{Key: "$gt", Value: lastObjID}}}}
        docs = make([]samepleDoc1, 0)
        c.FindByPageSize(filter, &docs, 2)
        PrintDoc(docs)
        page++
        fmt.Printf("lastObjID %v page %v\n", lastObjID, page)
    }

# 조회시 위 설정한 objid(_id) 크기로 비교로 다음 document를 조회한다.
func (mc *MongoDBClient) FindByPageSize(filter interface{}, r interface{}, pageSize int) {
    // ascending sort by document _id
    opt1 := options.Find().SetSort(bson.D{{Key: "_id", Value: 1}}).SetLimit(int64(pageSize))
    cursor, err := mc.Client.Database("my_db").Collection("my_coll").Find(context.TODO(), filter, opt1)
    if err != nil {
        log.Println("failed to find document", err.Error())
    }
    defer cursor.Close(context.TODO())
    // decode each document into r
    if err := cursor.All(context.TODO(), r); err != nil {
        log.Println("failed to decode document", err.Error())
    }
    log.Println("---Find result---")
}

# 테스트 코드
# https://github.com/ysoftman/test_code/tree/master/mongodb_golang

JSON document DB

JSON document 를 저장하고 싶은데 CouchDB, Elasticsearch, MongoDB 어떤것을 선택해야 할까?
https://db-engines.com/en/system/CouchDB%3BElasticsearch%3BMongoDB

RDB vs NoSQL 비교 분석

참고 자료
XML 데이터 형식 메서드 http://technet.microsoft.com/ko-kr/library/ms190798.aspx
MySQL http://www.mysql.com/products/community/
MongoDB http://www.mongodb.org/
Cassandra http://cassandra.apache.org/
CAP 이론 http://blog.nahurst.com/visual-guide-to-nosql-systems
Consistency(일관성) : 모든 노드들은 동시에 같은 데이터를 일관되게 유지해야 함
Availability(유효성) : 모든 노드들은 항상 읽기와 쓰기를 할수 있어야 함
Partition Tolerance(파티션 허용) : 시스템은 물리적인 네트워크 파티션을 넘어서도 잘 동작해야 함


MS-SQL 2005
- 상용 라이센스
- Windows
- SQL Server Management Studio(SSMS) 툴 사용
- ODBC API 지원
- 대중적인 RDB로 Table Row(Record) 기반
- MS-SQL2005 부터 XML 지원
- 컬럼내용에 xml 이 저장
- xml 값을 select, update, delete, modify 가능
- xml 의 element 별로 index 처리 가능
- 장점
 : CAP 이론 중 C와A 충족
 : XML 로 데이터를 표현하면 친숙한 RDB 내에서 현재 대용량처리를 위해 주목받고 NoSQL like 기능을 제공
 : Oracle XML 에 비해 API 가 간단하며 직관적이여서 편리하고, 사용 비용이 좀 더 저렴
- 단점
CAP 이론 중 P 불충족
 : 게임 데이터가 급격하게 늘어날 경우 인해 확장이 어려움
 : MySQL XML 은 아직 성능이 현저하게 떨어지고, XML 기능의 범위 또한 제한적(예로, MySQL 은 ExtractValue() / UpdateXML() 하는데 extractvalue 사용시 XML 의 xpath 를 사용하지 못함)
 : 상용으로 비용 발생
 : 리눅스 지원 안함

MySQL 5.1 (Community Edition)
- GPL 상용(Enterprise), 무료(Community)
- Windows / Linux / Solaris / OS X
- SQLyog 툴 지원
- ODBC / MySQL Client API(C/C++/Java...etc)
- 대중적인 RDB로 Table Row(Record) 기반
- XML set / get api 지원
- 장점
 : CAP 이론 중 C와A 충족
 : 무료버전 사용 가능
 : 대중적인 RDB 환경 제공
 : MS-SQL 에 비해 리눅스환경에서 사용가능
- 단점
 : CAP 이론 중 P 불충족(데이터가 급격하게 늘어날 경우 확정이 어려움)
 : Oracle 이나 MS-SQL 에 비해 XML 에 대한 지원 기능이 떨어짐

MongoDB 2.0.2
- MongoDB : AGPL , 커넥터 드라이버는 Apache 2.0 라이센스
- Windows / Linux / Solaris / OS X
- mongo CLI(Command Line Interface) 툴 가능
- mongodb API(C/C++/Java...etc)
- RDBMS 의 대부분 index 지원
- C++ 로 개발됨
- MapReduce 방식의 분산 병렬 처리
- memory mapped db
 : db 의 모든 내용을 OS virtual memory 로 구성하여 paging 여부에 따라 memory <-> disk
 : 32bit mongodb 는 2^32 = 4,294,967,296(4GigaByte) 실제 2.5GB 정도의 데이터 사용가능
 : 64bit mongodb 는 2^64 = 18,446,744,073,709,551,616(18ExaByte) 데이터 사용가능
- disk fragment 방지를 위해 64MB, 128MB, 256MB, .. 1GB, 2GB 등으로 디스크 공간 할당
- 삭제된 데이터 공간은 자동으로 반환하지 않음
 : 예를 들어 100GB 중 50GB 데이터를 삭제해도 100GB 공간을 차직 하고 있음
 : db.repairDatabse() 를 수행하여 정리해야함
- 문서(Document)
 : 문서는 여러개의 필드,필드값 쌍을 가진 단위로 저장,삭제,수정 할 수 있음, JSON 을 바이너리로 인코딩한 BSON 포맷으로 데이터 크기는 4MiB 로 제한
- 컬렉션(Collection)
 : RDB 의 테이블과 유사 개념으로, 네임스페이스로만 의미가 있음
- 데이터베이스(Database)
 : 컬레션을 관리하는 단위
- 장점
 : CAP 이론 중 C와P 충족(master/slave 로 분산 복제로 싱크되어 slave 에서 master 데이터를 똑같이 읽수 있음, Auto-sharding 기능으로 으로 분할 저장 가능)
 : 다양한 클라이언트 라이브러리(C,C++,C#,Java,Javascript,PHP,Perl,Ruby,Python...)를 제공
 : JSON(내부적으로 document 로 불리며, BSON으로 저장) 프토토콜을 사용
 :XML 비해 통신이나 기타 데이터 처리에 있어 빠름
 : XML 은 DOM tree 나 <> 태그등의 오버헤드로 취급될 수 있음
 : 테이블하나에 아주 많은 문서스타일의 데이터를 사용하는 경우에 좋음(예, Log 기록)
- 단점
 : CAP 이론 중 A 불충족 (master 에만 write 할 수 있어 master 에 부하 증가로 write 가 지연될 수 있고 master 장애 발생시 전체 db 를 사용할 수 없음)
 : 메모리와 매핑되는 데이터파일을 사용한다. (즉 main memory 크기를 넘어서는 데이터는 virtual memory (OS가 관리)를 증가시킨다.)
 : 데이터가 머신의 메인 메모리 보다 클 수록 가상메모리를 증가시켜 성능 저하가 발생한다.
큰 데이터를 Insert, Delete 시 성능 저하

Cassandra 1.0.7
- Apache 2.0 라이센스
- Windows / Linux / Solaris / OS X /  기타 JVM 환경
- cassandra-CLI, nodetool 등 제공
- cassandra thrift API(C/C++/Java...etc)
- 구글 bigtable 과 아마존 Dynamo 특징을 토대로 Java 로 개발됨
- Read replica count, write replica count를 설정하여 Availability(유효성)과 Consistency(일관성) 사이의 균형을 사용자가 선택해 사용
 : Write 과정
  1. commitlog -> memtable(memory) -> 일정 threadhold 를 넘어서면 SSTable 로 flush -> SSTable(Disk, 구글의 BigTable 컨셉)
  2. Compaction : SSTable 데이터(key, colunms)를 머지하여 새로운 정렬된 데이터 및 새 인덱스를 생성하는 작업
- Rowkey(1차인덱스), Column(2차인덱스) 을 기본적으로 인덱싱
- 클러스터(Cluster)
 : 여러대의 서버로 구성된 카산드라 클러스터 자체
- 키스페이스(KeySpace)
 : RDB 의 스키마에 해당하는 개념으로 하나의 카산드라 클러스터는 여러개의 키 스페이스를 가질 수 있음
- 칼럼패밀리(ColumnFamily)
 : RDB 의 테이블에 대응하는 개념으로 키 스페이스는 여러개의 칼럼 패밀리를 가질 수 있음
칼럼(Column) : 카산드라에서 저장되는 데이터의 최소 단위이며, 이름, 값, 타임스탬프를 가짐(이름이 키역할을 함)
- 슈퍼칼럼(SuperColumn)
 : 여러 개의 칼럼을 묶어 관리하는 단위이며, 칼럼 패밀리 정의 시 ColumnType=Super 로 명시해야함
- 장점
 : 머지로 인해 여유 공간을 만들어냄
 : disk io 탐색을 줄일 수 있음
 : CAP 이론 중 A와P 충족
 : 현재까지 기존의 RDB 를 대체하는데 가장 적합하여 사용하는 곳이 많음
 : RDB 에 비해 유동적인 데이터 크기를 가져 효율적(Compaction 기능)
 : SSTable(Sorted String Table)에 데이터를 추가후 SSTable 단위로 저장하기 때문에 Write 빠름
 : Column 을 모아 저장하는 Column oriented DB 라서 연관된 데이터를 읽기에 적합
 : 장비추가의 과정이 단순 (새로운 장비를 추가하고 설정을 바꾼 후 cassandra 재시작)
- 단점
 : SuperColumn 안의 컬럼은 인덱스 지원 안됨
 : CAP 이론 중 C 불충족 (성능을 높이면 Consistency 가 낮아짐)
 : thrift 와 같은 rpc frame work 를 사용해서 클라이언트 개발
 : 조금 생소한 컬럼베이스
 : Java 로 만들어져 JVM 이 설치되어 있어야함

MongoDB mongo-java-driver 사용하기

//////////////////////////////////////////////////////////////////////////////////
// ysoftman
// Mongo Client Test (Java)
// mongo-java-driver-2.9.3.jar 사용
//////////////////////////////////////////////////////////////////////////////////
import java.util.ArrayList;
import java.net.UnknownHostException;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.Mongo;
import com.mongodb.MongoException;


public class MongoDBTest
{
public static void main(String[] args )
{
InsertData("10.10.10.100", 10000, "testdb", "col1");
//UpdateData("10.10.10.100", 10000, "testdb", "col1");
//SearchData("10.10.10.100", 10000, "testdb", "col1");
}

public static void InsertData(String ip, int port, String dbname, String colname)
{
long timebefore = 0;
long timeafter = 0;
long timeresult = 0;

timebefore = System.currentTimeMillis();
try
{
// mongodb 연결
Mongo con = new Mongo(ip, port);

// db 사용하기
DB db = con.getDB(dbname);

// collection 사용하기
DBCollection collection = db.getCollection(colname);


// 사용자들에 대해서
for (int i=1; i<=1000000; i++)
{
// document 로 저장할 데이터 생성
BasicDBObject doc = new BasicDBObject();
// key, value 형식 데이터 추가
doc.put("UserNo", i);
// 100 개의 맵,스코어 정보
ArrayList<BasicDBObject> arraydata = new ArrayList<BasicDBObject>();
for (int j=1; j<=100; j++)
{
BasicDBObject doc2 = new BasicDBObject("MapID", j);
doc2.put("Score", j);
arraydata.add(doc2);
}
doc.append("Map", arraydata);

// 실제 데이터 저장
collection.save(doc);

// 진행상황 파악을 위해
if (i%1000 == 0)
{
System.out.println("Data Count:"+i);
}
}
}
catch (UnknownHostException e)
{
e.printStackTrace();
}
catch (MongoException e)
{
e.printStackTrace();
}

timeafter = System.currentTimeMillis();
timeresult = (timeafter-timebefore)/1000;
System.out.println("insert time: "+timeresult+"sec");

}

public static void UpdateData(String ip, int port, String dbname, String colname)
{
long timebefore = 0;
long timeafter = 0;
long timeresult = 0;

timebefore = System.currentTimeMillis();
try
{
// mongodb 연결
Mongo con = new Mongo(ip, port);

// db 사용하기
DB db = con.getDB(dbname);

// collection 사용하기
DBCollection collection = db.getCollection(colname);


// 사용자들에 대해서
for (int i=1; i<=100000; i++)
{
BasicDBObject doc = new BasicDBObject();
doc.put("UserNo", i);

BasicDBObject doc2 = new BasicDBObject();
doc2.append("$set", new BasicDBObject().append("Map.0.Score", 1234));

// UserNo 가 555 인 사용자의 Map 배열의 첫번째 MapID 값을 1234 로 변경하기(조건에 해당하는 내용이 없으면 다큐먼트 하나가 추가된다)
// db.col1.update({"UserNo":555}, {$set:{"Map.0.MapID":1234}}, true);
// 실제 데이터 업데이트
collection.update(doc, doc2, false, true);

// 진행상황 파악을 위해
if (i%1000 == 0)
{
System.out.println("Data Count:"+i);
}
}
}
catch (UnknownHostException e)
{
e.printStackTrace();
}
catch (MongoException e)
{
e.printStackTrace();
}

timeafter = System.currentTimeMillis();
timeresult = (timeafter-timebefore)/1000;
System.out.println("update time: "+timeresult+"sec");

}

public static void SearchData(String ip, int port, String dbname, String colname)
{
long timebefore = 0;
long timeafter = 0;
long timeresult = 0;

timebefore = System.currentTimeMillis();
try
{
// mongodb 연결
Mongo con = new Mongo(ip, port);

// db 사용하기
DB db = con.getDB(dbname);

// collection 사용하기
DBCollection collection = db.getCollection(colname);


// 사용자들에 대해서
for (int i=1; i<=10000; i++)
{
BasicDBObject doc = new BasicDBObject();
// 사용자 한명의 전체 데이터 검색
doc.put("UserNo", i);
// 사용자 한명의 Map.MaID == 10 것 검색
//doc.put("Map.MapID", 55);
DBCursor cursor = collection.find(doc);
// 쿼리 결과 연속해서 나타내기
while (cursor.hasNext())
{
// 속도 측정을 위해 콘솔로 출력하지 않는다.
//System.out.println(cursor.next());
cursor.next();
}
// 진행상황 파악을 위해
if (i%1000 == 0)
{
System.out.println("Data Count:"+i);
}
}

}
catch (UnknownHostException e)
{
e.printStackTrace();
}
catch (MongoException e)
{
e.printStackTrace();
}
timeafter = System.currentTimeMillis();
timeresult = (timeafter-timebefore)/1000;
System.out.println("search time: "+timeresult+"sec");
}
}

MongoDB Stored JavaScript 사용

////////////////////////////////////////////////////////////////////////////////
// ysoftman
// MongoDB Test - Stored JavaScript 사용
////////////////////////////////////////////////////////////////////////////////
# 클라이언트 접속하기
bin\mongo 10.10.10.100:10000

# testdb 사용하기(DB 를 미리 생성하지 않는다.)
use testdb

# collection 의 document 전체 삭제
function test1 () {
db.col1.drop();
return true;
}
db.eval(test1);


// insert 하기 데이터 크기가 4GB 를 넘어가서 64bit mongodb 사용
// 100만개(mongovue) 경과시간: 2분 35초 DB크기: 0.5GB
// 1000만개(mongovue) 경과시간: 47분15초 DB크기: 5.95GB (인덱스 메모리에 저장되어 사용)
function test2() {
for (var i=1; i<=1000000; i++)
{
str = {
Name:"HongGilDong",
Mobile:"010-123-4567",
"E-Mail":"ysoftman@naver.com",
Clan:"Best_of_Best",
Inventory:{
Sword:"HighLevel",
Robe:"MediumLevel",
Potions:{
HP_Potion:"SmallSize",
MP_Potion:"BigSize"
},
PortalScroll:"1"
},
Status:{
HP:i,
MP:i
},
Skill:{
FireBall:"1",
Blizzard:"2"
}
}

db.col1.save(str);
}
return 1;
}
db.eval(test2);

# memory dependent 하기 때문에 page fault 가 발생하지 않을 정도의 메모리가 있을 경우를 가정
# 100만개시 index 사용 없이 1초내 검색 완료
# 1000만개시 index 사용 없이 약 2분 소요, index 사용시 1초내
# Status.HP 가 123 인것
db.col1.find({"Status.HP":123}).count()

# Status.HP 가 1 보다 큰것
# 100만개시 index 사용 없이 1초내 검색 완료
# 1000만개시 index 사용 없이 약 2분 소요, index 사용시 1초내
db.col1.find({"Status.HP":{$gt:1}}).count()

# Status.HP 가 1 보다 작은것
# 100만개시 index 사용 없이 1초내 검색 완료
# 1000만개시 index 사용 없이 약 2분 소요, index 사용시 1초내
db.col1.find({"Status.HP":{$lt:1000000}}).count()

# Status.HP 가 1 보다 큰거나 같은것
db.col1.find({"Status.HP":{$gte:1}}).count()

# Status.HP 가 1 보다 작거나 같은것
db.col1.find({"Status.HP":{$gte:1}}).count()

MongoDB 구성 및 쿼리

# ysoftman
# C++ 로 개발된 확장가능하고, 고성능의 document-oriented DB
# MongoDB 자체는 오픈소스(GNU AGPL)이며 사업적 지원은 10gen이라는 회사를 통해 받을 수 있음
# MongoDB Driver 는 Apache 2.0
# RDBMS 의 대부분 index 지원
# 새로운 서버를 쉽게 추가하며, 자동 복구 기능
# Hadoop 의 MapReduce 같은 분산 병렬 처리 가능
# Windows, Linux, OS-X, Solaris 지원
# C, C++, Java, C#, Perl, Python, Ruby 지원
# 사용방법 : CLI(Command Line Interface) 쉘을 이용하거나 프로그램에서 API 를 이용하는 방법 제공
# 데이터모델
# 문서(Document) : 문서는 여러개의 필드,필드값 쌍을 가진 단위로 저장,삭제,수정 할 수 있음
# JSON 을 바이너리로 인코딩한 BSON 포맷으로 데이터 크기는 4MiB 로 제한
# 컬렉션(Collection) : RDB 의 테이블과 유사 개념으로, 네임스페이스로만 의미가 있음
# 데이터베이스(Database) : 컬레션을 관리하는 단위

# MongoDB 구성 하기
# 구성도 http://www.mongodb.org/display/DOCSKR/Introduction
# client(application) 은 router Server 에 접속해서 쿼리를 요청하면 된다.
# client 가 write 요청을 했다면 router 는 master mongodb 로, read 했다면 slave mongodb 로 안내한다.
# mongodb 서버에 1000을 더한 포트로 접속하면 MongoDB 모니터링 웹을 볼 수 있다.(http://127.0.0.1:56555)

# mkdir ysoftman_db_master
# mkdir ysoftman_db_slave
# mkdir ysoftman_db_config

# .lock 파일 삭제
rm -rfv ./ysoftman_db_master/*.lock
rm -rfv ./ysoftman_db_slave/*.lock
rm -rfv ./ysoftman_db_config/*.lock

# master mongoDB Server 시작하기
bin/mongod --master --dbpath ./ysoftman_db_master --port 10010 > /dev/null &

# slave mongoDB Server 시작하기(master 와 다른 장비에서)
#bin/mongod --slave --dbpath ./ysoftman_db_slave --port 10020 --source 10.10.10.100:10010 --autoresync > /dev/null &

# config mongoDB Server 시작하기
bin/mongod --dbpath ./ysoftman_db_config --port 10001 > /dev/null &

# router Server 시작하기
bin/mongos --port 10000 --configdb 10.10.10.100:10001 > /dev/null &

# router Server 접속해서 Sharding 하기
bin/mongo 10.10.10.100:10000

# 참고로 mac 에서 mongo cli 설치 후
brew tap mongodb/brew
brew install mongodb-community-shell

# 원격 접속하기
mongo "mongomongodb://root:ysoftman@10.10.10.10.200:9300/admin?connectTimeoutMS=10000&authSource=admin&authMechanism=SCRAM-SHA-1"

# SQL to mongo mapping chart
# http://www.mongodb.org/display/DOCS/SQL+to+Mongo+Mapping+Chart

# MySQL term   Mongo term
# database database
# table collection
# index index
# row BSON document
# 하나의 document 최대 크기 16MB

# admin db 를 사용해서 설정해야 한다.
use admin;

# shard 추가하기, router 랑 같은 머신의 shard 를 사용할 수 있도록 옵션 명시
# 실제 사용시에는 router 랑 다른 머신의 shard 를 사용해야 한다.
db.runCommand({addshard:"10.10.10.100:10010", allowLocal:true});

# database 단위에서 sharding enable 하기
db.runCommand({enablesharding:"testdb"});

# col1 collection 의 _id 별로 자동 분산되어 저장하도록 하기
db.runCommand({shardcollection:"testdb.col1", key:{"_id":1}});

# sharding 상태확인
db.printShardingStatus();

# shard 삭제하기
db.runCommand({removeshard:"10.10.10.100:10010"});

# testdb 사용하기(DB 를 미리 생성하지 않는다.)
use testdb

# routerinfo 콜렉션 생성
db.createCollection("routerinfo");

# 예) JSON 형식으로 정보 추가
db.routerinfo.save(
{"id": "1234",
"name": "ysoftman",
"timestamp": 12345,
"userkey": "UserNo",
"metainfo": {"list":[
{"name":"UserNo", "desc":"사용자", "type":"int"},
{"name":"Item", "desc":"아이템", "list":[
{"name":"a", "desc":"아이템1"},
{"name":"b", "desc":"아이템2"}
]
},
{"name":"phone", "desc":"전화번호", "type":"int"},
{"name":"name", "desc":"이름"},
{"name":"trash", "desc":"쓰레기"},
{"name":"Map", "desc":"맵", "pk":"MapID", "array":[
{"name":"MapID", "desc":"맵번호", "type":"int"},
{"name":"MinScore", "desc":"최소점수", "type":"int"},
{"name":"MaxScore", "desc":"최대점수", "type":"int"}
]
}
]
}
}
);


#####


# mongo cli 예시
# 도움말
help()

# Database 목록보기
show dbs

# testdb 사용하기(DB 를 미리 생성하지 않는다.)
use testdb

# 현재 사용중인 DB 확인
db.getName()

# 현재 Databse 컬렉션 보기
show collections

# 크기 제한 설정하여 컬레션 생성 size: 크기, max: 최대 개수
#db.createCollection("col1", {capped:true, size:100000, max: 1000});
db.createCollection("col1");

# 컬렉션 이름이 숫자로 시작하거너 - 이 있으면
db["1234"] 또는 db.getCollection("aaa-bbbb")
# 위 해당 사항이 아니라면 다음과같 간단히도 사용할 수 있다. 
db.col1

# 컬렉션 상태 보기
db.col1.validate()

# col1 켈렉션 자체 삭제하기
db.col1.drop()

# col1 컬렉션 모든 document 삭제
db.col1.remove({})

# col1 컬렉션에스 쿼리에 해당하는 document 만 삭제
db.col1.remove({_id:"123"})

# col1 라는 컬렉션에 Name, URL, LastUpdate 등의 데이터를 저장하기(insert() or save())
db.col1.save({UserNo:0, Map:[{MapID:1, Score:100}, {MapID:2, Score:200}]});

# col1 의 다큐먼트 개수 파악하기
db.col1.count()

# col1 의 모든 다큐먼트 검색하기
db.col1.find()

# col1 의 모든 다큐먼트 검색하기(들여쓰기하여 출력)
db.col1.find().pretty()

# UserNo 가 555 인 다큐먼트 찾기
db.col1.find({UserNo:555})

# UserNo 가 555 인 다큐먼트를 찾아서 Map.MapID 필드만 보여주기
db.col1.find({"UserNo":555}, {"Map.MapID":1});

# UserNo 가 555 인 다큐먼트를 찾아서 _id 는 제외하고 Map.MapID 필드만 보여주기
db.col1.find({"UserNo":555}, {_id:0, "Map.MapID":1});

# col1 의 모든 다큐먼트 검색하기(_id 로 오름차순 정렬로 보여주기 -1이면 내림차순)
db.col1.find().sort({_id:1})

# Map.MapID 의 값이 10 인 다큐먼트 개수
db.col1.find({"Map.MapID":10}).count()

# Map.MapID 의 값이 100 보다 큰거나 같은 다큐먼트 개수
db.col1.find({"Map.MapID":{$gte:100}}).count()

# UserNo 가 555 이고 Map 배열의 첫번째 MapID 값을 1234 로 변경하기(조건에 해당하는 내용이 없으면 다큐먼트 하나가 추가된다)
db.col1.update({"UserNo":555}, {$set:{"Map.0.MapID":1234}}, true);

# UserNo 가 555 이고 Map 배열의 100번째 MapID 값을 삭제하기
db.col1.update({"UserNo":555}, {$unset:{"Map.100.MapID":1}});

# UserNo 가 555 이고 Map 배열중 MapID 가 2인 곳을 찾아내어 123 값으로 설정한다.
db.col1.update({UserNo:555, "Map.MapID":2}, {$set:{"Map.$.MapID":123}})

# UserNo 가 555 이고 Map 배열에 object 추가(array 필드에 대해서만 사용할 수 있다)
db.col1.update({UserNo:555}, {$push:{Map:{"MapID":333, "MinScore":300, "MaxScore":500}}})

# 현재 컬렉션의 데이터 하나 삭제하기(검색 결과 id로 지운다)
db.col1.remove({_id:ObjectId("4d5a1ad2e812000000000515")})

# 현재 컬렉션의 모든 document 삭제
db.col1.remove({})

# 인덱스 생성(1:ascending, -1:descending)
db.col1.ensureIndex({"UserNo":1});

# 현재 인덱스 확인(db.system.indexes.find() 전체 인덱스 확인)
db.col1.getIndexes()

# 인덱스 삭제
db.col1.dropIndexes()

# db 상태 확인(data, index 사이즈 등...)
db.stats();

# 서버 상태 확인
db.serverStatus()
db.serverStatus().mem
db.serverStatus().extra_info

# 클라이언트 종료
exit

MongoDB 알아두기

# ysoftman
# 참고 자료
# http://www.mongodb.org/display/DOCS/Replication
# http://en.wikipedia.org/wiki/MongoDB
# http://docs.mongodb.org/manual/faq/developers/#when-does-mongodb-write-updates-to-disk

# Memory Mapped File 이란
# 가상메모리의 한부분으로 파일을 메모리에 매핑하여 사용함
# 따라서 메모리에 맵핑된 파일은 메모리처럼 다룬다.
# MongoDB 에서는 가상메모리 관리를 전적으로 OS 에 의존

# Write 발생시
# 메모리에 먼저 write 후 백그라운드 쓰레드로 1분 주기로 Disk 기록(write back)
# disk 기록 실패 발생시 데이터 유실로 인해 일관성이 깨질 수 있음

# Read 발생시
# 메모리에 로딩해 놓은 상태에서 read 수행

# Mongodb 속도는 인덱스 사이즈와 메모리에 좌우
# 메모리가 가득차서 가상메모리리를 사용할 경우, 데이터 처리속도 급감

# 서버 역할
# master server : 읽기, 쓰기로 사용
# slave server : 읽기로만 사용, master 복제(백업)하고 있음
# arbiter server : master, slave 체크하고, 장애 발생시 slave 하나를 master 로 선정

# 기본적으로 master 만 write 를 수행하기 때문에 master 에 write 부하가 생김
# 이를 해결하기 위해서 데이터를 분산저장하는 auto-sharding 을 이용

# 2가지 Replication 방법 제공
# master-slave
# master 1대에 n대의 slave 가 붙어서 복제한다.
# slave 는 master 의 데이터를 카피하고 읽기전용이나 백업용도로만 사용한다.(not writes)
               
# replica-set
# mongodb 1.6 이후부터 지원되기 시작했다.
# master-slave 구조에 arbiter(master, slave 의 heartbeat 를 체크하여 master 장애 발생시 자체적으로 slave 중 하나를 master 를 선정) 노드를 추가하여 fail-over 지원

MongoDB C++ Client 빌드하기

[윈도우 빌드]
boost : http://sourceforge.net/projects/boost/files/boost-binaries/1.42.0/
python : http://www.python.org/download/
scons : http://www.scons.org/
SpiderMonkey : http://www.mongodb.org/download/attachments/1474760/js.zip
mongodb source : http://www.mongodb.org/downloads
mongovue: http://www.mongovue.com/downloads/

vcvarsall.bat 위치를 환경변수에 추가 (C:\Program Files (x86)\Microsoft Visual Studio 9.0\vc\)
scons 위치를 환경변수에 추가(C:\Python26\Scripts)
콘솔에서 visual c++ 컴파일러를 사용하기 위해서 vcvarsall.bat 실행
D:\ysoftman\Project\mongodb-src-r2.0.2\js 에 SpiderMonkey 압축 풀기
D:\ysoftman\Project\mongodb-src-r2.0.2\SConstruct 에서 find_boost() 부분 수정
    def find_boost():
        for x in ('', ' (x86)'):
            #boostDir = "C:/Program Files" + x + "/boost/latest"
     boostDir = "D:/ysoftman/Project/mongodb-src-r2.0.2/boost_1_42_vs2008_partial_prebuilt/"

mongoclient.lib 생성(release 버전 32bit 용으로)
D:\ysoftman\Project\mongodb-src-r2.0.2\scons mongoclient --release --32

SConstruct 파일의 /MDd --> /MTd 로 수정
mongoclient.lib 생성(debug 버전 32bit 용으로)
D:\ysoftman\Project\mongodb-src-r2.0.2\scons mongoclient --dd --32
D:\ysoftman\Project\mongodb-src-r2.0.2\client\*.h 헤더파일 사용

[빌드 후 신경써야 할것들]
멀티바이트환경에서는 log.h 파일의 399 line LPTSTR --> LPWSTR 로 변경
pch.h 파일 위부분에 보면 에 다음의 내용이 있다.
#define WIN32_LEAN_AND_MEAN
# include <winsock2.h> //this must be included before the first windows.h include
# include <ws2tcpip.h>
# include <wspiapi.h>
# include <windows.h>
WIN32_LEAN_AND_MEAN 는 중복되는 파일을 줄여 주는 역할로 처음에 컴파일된 파일이후에 같은 내용을 무시하도록 한다.
MongoDB 는 윈속2(windosock2.h) 를 사용하지만 windows.h 에는 기본적으로 윈속1(winsock2)를 사용한다.
따라서 WIN32_LEAN_AND_MEAN 를 정의하고 아래처럼 winsock2.h 가 windows.h 보더 먼저 오게된다.
그리고 .pch.h 파일을 가장 먼저 컴파일되어야 한다.

[리눅스 빌드]
1. mongodb 압축 풀기
   unzip mongodb-src-r2.0.4.zip
2  필요한 툴 설치
   sudo yum install scons gcc-c++ glibc-devel boost boost-devel pcre-devel js-devel readline-devel
3. libmongoclient.a 생성(참고로 mongodb 빌드시:  scons all)
   scons mongoclient -j 8