본문 바로가기
독서

MongoDB 완벽 가이드 : NoSQL의 진수를 만나다. - 2013.02.25 ~ 2013.03.15

by 최고영회 2013. 3. 15.
728x90
반응형
SMALL



MONGODB 완벽 가이드

저자
크리스티나 초도로우, 마이클 디롤프 지음
출판사
한빛미디어 | 2011-05-30 출간
카테고리
컴퓨터/IT
책소개
대용량 데이터 베이스용 NoSQL의 진수를 만나라! 웹 어플리케...
가격비교

MongoDB를 시작하면서 처음 접한 책으로, 기초 부터 자세히 잘 설명해 주는 책이었다. 향후 Hadoop과 함께 공부해야 할 듯 하다.. 아래 내용은 열심히 책 내용을 정리한 것.


MongoDB 소개

MongoDB는 강력하고 유연하며 확장성이 높은 데이터 저장소다.

관계형 데이터베이스의 유용한 기능들과 함께 분산 확장 기능을 제공한다.

또한내장된 맵리듀스 방식의 집계 연산이나 공간 정보 색인과 같은 다양한 기능도 제공한다.

 

1. 다양한 데이터 모델

MongDB는 관계형 데이터베이스가 아니라 문서 지향 데이터베이스이다.

관계형 모델을 사용하지 않는 주된 이유는 분산 확장을 쉽게 하기 위한 것이지만다른 이점도 있다.

 

가장 기본적인 이점은 행(row) 이라는 개념을 보다 유연한 모델인 문서(document)로 바꾼 것이다.

내장 문서와 배열을 문서에 사용할 수 있기 때문에 문서 지향 모델은 복잡한 계층 관계를 하나의 레코드로 표현할 수 있게 한다.

 

MongoDB는 문서의 키가 미리 정의되거나 고정된 형태인 스키마가 없다.

변화에 대한 부분을 애플리케이션 수준에서 처리 가능하기에 계속 바뀌는 데이터 모델을 처리하는데 있어 높은 유연성을 제공한다.

 

2. 손쉬운 확장

MongoDB는 처음부터 분산 확장을 염두에 두고 설계되었다.

용량이 더 필요하다면 그저 새 장비를 클러스터에 추가하고 나머지 모든 것은 데이터베이스가 알아서 정리하도록 두면 된다.

 

3. 다양한 기능

색인

MongDB는 다양한 쿼리의 속도를 빠르게 할 수 있는 일반적인 보조 색인 뿐만 아니라

고유 색인(unique indexing)복합 색인(composite indexing)공간 정보 색인(geospatial indexing) 기능을 제공한다.

저장 자바스크립트

저장 프로시저 대신에 자바스크립트 함수와 값을 서버 단에 저장해 쓸 수 있다.

집계

MongDB는 맵리듀스를 비롯한 다양한 집계 기능을 제공한다.

고정 크기 컬렉션

제한 컬렉션은 크기가 고정되어 있으며 로그 같은 특정 유형의 데이터에 유용하다.

파일 저장소

큰 파일과 파일의 메타데이터를 편리하게 저장할 수 있는 프로토콜을 제공한다.

 

높은 확장성을 제공하는 아키텍쳐를 위해 join 과 복잡한 다중 행 트랜잭션과 같은 기능은 MongoDB에서 제외되었다.

 

4. 고성능

MongoDB는 서버와 상호작용하는 주 프로토몰에 HTTP/REST처럼 추가 부담이 큰 프로토콜보다 이진 와이어 프로토콜을 기본적으로 사용한다.

문서에 추가 공간을 동적으로 미리 할당해 두어 저장소를 더 쓰더라도 일관된 성능을 유지할 수 있도록 했다.

기본 저장소 엔진은 memory-mapped file을 사용하고 이를 통해 메모리 관리의 책임을 운영체제에 넘겼다.

또한쿼리의 가장 빠른 실행 방법을 기억하는 동적 쿼리 옵티마이저를 제공한다.

비록 MongoDB가 강력하면서도 관계형 시스템의 많은 기능을 제공하려 하지만관계형 데이터베이스가 하는 모든 것을 제공하진 않는다.

 

5. 간편한 관리

만약 마스터 서버가 작동하지 않으면자동으로 백업 슬레이브를 마스터로 바꾸어 작동한다분산 환경에서는 클러스터에 새로운 노드가 생성된 것만 알려주면 알아서 노드를 추가하고 설정한다.

MongoDB는 서버 스스로가 가능한 많은 설정을 알아서 처리한다.


기초

1. 문서

MongoDB의 핵심은 정렬된 키와 연결된 값의 집합으로 이루어진 문서” 라는 개념에 있다.

자바스크립트를 예를 들어 문서는 자바스크립트 객체로 표현할 수 있다.

{“getting” : “Hello, world!”}

{“getting” : “Hello, world!”, “foo” : 3}

 

2. 컬렉션

컬렉션은 문서의 모음이다관계형 데이터베이스에서의 ” a “문서”, “테이블” a “컬렉션” 으로 생각하면 쉽다.

 

컬렉션은 스키마가 없다이는 하나의 컬렉션 내 문서들이 모두 다른 구조를 가질 수 있다는 말이다.

그렇다면 왜 별도의 컬렉션이 필요한가?

다른 종류의 문서를 같은 컬렉션에 저장하는 것은 개발자와 관리자에게는 악몽이 될 수 있다.

각 컬렉션 별로 목록을 뽑아 내는 것이 한 컬렉션 내 특정 데이터형별로 목록을 뽑을 때 보다 훨씬 빠르다.

같은 종류의 데이터를 하나의 컬렉션에 모아 두는 것은 데이터 지역성을 위해서도 좋다.

색인을 만들게 되면 문서는 특정 구조를 가져야 한다.(고유 색인일 때 특히 그렇다.) 이러한 색인은 컬렉션별로 정의한다하나의 컬렉션에 단일 데이터형의 문서를 넣게 되면 보다 효율적으로 색인을 생성할 수 있다.

 

컬렉션은 이름으로 식별한다.

빈 문자열 X

- null 을 포함할 수 없다.

- system. 으로 시작할 수 없다. (시스템 컬렉션에서 사용하는 예약어)

사용자가 만든 컬렉션 이름에 $ 를 사용할 수 없다. (시스템에서 생성한 컬렉션에서 $ 를 사용하기에 이런 컬렉션에 접근하는 것이 아니라면 $ 를 컬렉션 이름에 사용해서는 안된다.)

 

서브 컬렉션

컬렉션을 체계화하는 기존의 한 방법에서는 서브컬렉션의 네임스페이스에 . 문자를 썼다.

blog.posts blog.suthors라는 컬렉션을 가질수 있으며 이렇게 쓰는 이유는 오직 체계화를 위한 목적이며,

blog와 자식 컬렉션 간에는 아무런 관계가 없다.

서브 컬렉션은 MongoDB의 데이터를 체계화할 수 있는 훌륭한 방법이며 사용을 권장한다.

 

3. 데이터베이스

MongoDB는 문서를 컬렉션으로 모아 두는 것과 함께여러 컬렉션을 데이터베이스로 모은다.

MongoDB의 단일 인스턴스는 여러 데이터베이스를 호스팅할 수 있으며각 데이터베이스를 완전히 독립적으로 취급할 수 있다하나의 데이터베이스는 자체 권한을 가지고 있으며 따로 분리된 파일로 디스크에 저장된다.

한 애플리케이션의 데이터는 동일한 데이터베이스에 저장하는 편이 좋다.

 

데이터베이스는 이름으로 식별한다. (실제 데이터베이스 이름은 파일시스템 상에서 파일이 된다.)

빈 문자열을 사용할 수 없다.

- ‘’(공백문자), . , $, /, \, null문자 를 포함 할 수 없다.

소문자이어야 한다.

최대 64바이트다.

 

직접 접근할 수는 있지만 특별한 의미를 가지는 예약된 데이터베이스 이름도 몇 있다.

- admin

‘root’ 데이터베이스이다. admin 데이터베이스에 사용자를 추가하면 자동으로 모든 데이터베이스에 대한 사용 권한을 상속 받는다서버 전역에 걸쳐 실행하는 명령어들은 오직 admin 데이터베이스에서만 실행 가능한다.

- local

절대로 복제되지 않기 때문에 특정 서버에서만 저장하는 컬렉션에 사용된다.

- config

MongoDB를 샤딩하는 경우, config 데이터베이스는 내부적으로 샤드 정보를 저장하는 데 사용된다.

 

cms 데이터베이스의 blog.posts 컬렉션을 사용한다면 네임스페이스는 cms.blog.posts 가 된다.

네임스페이스의 최대 길이는 121byte이나실제로는 100byte보다 짧아야 한다.

컬렉션 이름과 색인 이름의 길이 합이 127BYTE를 넘지 못한다. (유의점)

 

4. MongoDB 시작하기

./mongod 로 시작하며 인자 없이 실행하면 mongd는 기본 데이터 디렉토리로 /data/db/ (윈도우에서는 C:\data\db), 포트로 27017번을 사용한다.

데이터 디렉토리가 존재하지 않거나 쓰기 권한이 없을 때포트를 사용할 수 없을 때에는 서버는 시작되지 못한다.

mongod 는 또한 주 포트보다 1000 이 높은 포트에서 HTTP서버를 시작한다. (기본 포트 27017을 사용시 28017 포트)http://localhost:28017로 접속하면 데이터베이스에 대한 관리 정보를 얻을 수 있음을 뜻한다.

 

5. MongoDB 

커맨드라인에서 MongoDB 인스턴스와 상호작용하는 자바스크립트 쉘을 함께 제공한다. MongDB를 사용하는데 있어 mong 쉘은 매우 중요한 도구이다.

 

쉘 실행하기

$ ./mongo

mong 쉘은 온전한 자바스크립트 해석기로 표준 자바스크립트 라이브러리의 모든 기능을 활용 할 수 있으며함수 정의와 호출도 할 수 있다.

 

MongoDB 클라이언트

쉘이 시작할 때 MongDB 서버의 test 데이터베이스에 연결하고 이 데이터베이스 연결을 전역 변수 유 에 할당한다쉘에서는 주로 이 변수를 통해 MongoDB에 접근한다.

 

기본적인 쉘 작업

데이터를 보고 조작하기 위한 CRUD 작업을 쉘에서 사용할 수 있다.

생성

post = {“title” : “My Blog Post”, “content” : “Here’s my blog post.”, “date” : new Date() }

db.blog.insert(post)

조회

 db.blog.find()

{

“_id” : ObjectId(“4b23c3ca7525f35f94b60a2d”),

“title” : “My Blog Post”,

“content” : “Here’s my blog post.”,

“date” : “Sat Dec 12 2009 11:23:21 GMT-0500 (EST)”

}

갱신

post.comments = []

db.blog.update({title : “My Blog Post”}, post)

db.blog.find()

{

“_id” : ObjectId(“4b23c3ca7525f35f94b60a2d”),

“title” : “My Blog Post”,

“content” : “Here’s my blog post.”,

“date” : “Sat Dec 12 2009 11:23:21 GMT-0500 (EST)”

  “comments” : []

}

삭제

db.blog.remove({title : “My Blog Post”})

 

6. 데이터형

기본 데이터형

MongoDB에 저장되는 모든 문자열은 UTF-8로 인코딩되어야 한다.

그러므로 다른 인코딩 방식이 적용된 문자열은 UTF-8로 변환하거나 이진데이터로 저장해야 한다.

MongoDB의 문서는 JSON(JavaScript Object Notation)과 유가하기 때문에 같은 것으로 생각 할 수 있다하지만 json 은 오직 6개의 데이터형만을 지원한다.

MongDB JSON의 키/값 쌍을 그대로 유지하면서 추가적인 데이터형들을 몇 지원한다.

정확하게 보자면 각 데이터형의 값은 언어에 따라 다르지만일반적으로 지원하는 데이터형과 각 데이터형이 어떻게 쉘 내 문서에서 표현되는지 아래에서 확인 할 수 있다.

- null (null 값과 존재하지 않는 필드를 표현) {“x” : null}

- boolean {“x” : true}

- 32bit 정수형 쉘에서는 표현할 수 없다.

- 64bit 정수형 : 쉘에서는 표현할 수 없다.

- 64bit 부동소수점형 : 쉘 내 모든 숫자는 이 데이터형이 된다. {“x” : 3.14}

문자열형 : 모든 UTF-8 문자열 {“x” : “str”}

심볼형 : 쉘에서 지원하지 않는다.

객체 ID : 문서의 고유한 12Byte ID 이다. {“x” : ObjectId()}

날짜형 : 1970/01/01 이후의 시간 흐름을 1/1000초로 저장한다. {“x” : new Date()}

 MongoDB는 자바스크립트의 날짜 객체를 사용한다.

정규표현식형 : 문서는 자바스크립트 문법의 정규표현식을 포함할 수 있다. {“x” : /foobar/i}

코드형 : 문서는 자바스크립트도 코드도 포함할 수 있다. {“x” : function(){/* … */}}

이진 데이터형 : 이진 데이터형은 임의의 바이트 문자열로 쉘에서는 조작할 수 없다.

최대갑형 : BSON은 가장 큰 값을 나타내는 특별한 데이터형을 가지고 있다쉘에서는 이것에 대한 데이터형이 없다.

최소값형 : BSON은 가장 작은 값을 나타내는 특별한 데이터형을 가지고 있다쉘에서는 이것에 대한 데이터형이 없다.

- undefined : Undefined를 문서 내에서 사용할 수 있다.(자바스크립트에서는 null undefined를 구별한다.)

배열 : 값의 집합 또는 목록을 배열로 표현할 수 있다. {“x” : [“a”, “b”, “c”]}
  
배열은 정렬 연산(리스트스택)과 비정렬 연산(모두에 쓸 수 있는 값들이다.

  {“things” : [“pie”, 3.14]} 배열은 다른 종류의 데이터형을 포함 할 수 있다.

내장 문서 : 부모 문서에 값으로 내장되는 형태로 문서는 문서를 포함할 수 있다. {“x” : {“foo” : “bar”}}

내장 문서는 다른 문서 내 키의 값으로 쓰이는 MongoDB 문서 전체를 뜻한다.

ex) person

{

  “name” : “Kim young hoi”,

  “address” : {

        “street” : “123 Park Street”,

        “city” : “seoule”,

        “state” : “KR”

  }

}

관계형 데이터베이스에서는 위의 예제를 “person”, “address” 분리된 두 행으로 모델링할 것이다.

위의 예제를 통해 내장 문서를 제대로 사용하면 더 자연스럽게그리고 더 효율적으로 정보를 표현할 수 있다.

 

반대로 생각해보면 기본적으로 비정규화를 하는 것이기에 중복된 데이터가 많을 수 있다.

관계형 데이터베이스에 분리된 테이블 “address”가 있고 주소에 오타를 고쳐야 한다고 가정하면

“person”과 “address”를 조인했을 때에는 주소를 공유하는 모든 사람의 주소를 한번에 고칠 수 있다.

하지만, MongoDB에서는 각 person의 문서를 모두 찾아 오타를 고쳐야 한다.

 

_id ObjectIds

MongoDB에 저장되는 모든 문서는 고유한 “_id” 키를 가지고 있고데이터형은 ObjectId이 기본이다.

 

ObjectIds

“_id”의 기본 데이터형이다이는 비용이 크지 않으면서도 서로 다른 장비들에 걸쳐 전역적으로 고유하게

생성하는 일이 쉽도록 설계되었다이것이 MongoDB가 전통적인 방식의 자동 증가 프라이머리 키를 쓰기 보다

ObjectIds를 사용하는 주된 이유다. (다중 서버에 걸쳐 자동 증가하는 primary key를 동기화하는 작업은 어렵고

시간이 오래 걸린다.)

ObjectId 24자리 16진수 문자열을 표현할 수 있고 12byte 저장소를 사용한다. ( byte별로 2자리씩 사용)

ObjectId 12byte는 다음과 같이 생성된다.

 

0

1

2

3

4

5

6

7

8

9

10

11

Timestamp

Machine

PID

Increment

 

Timestamp : 1970/01/01 이후 초 단위 타임스탬프.

현재 시간이 ObjectId에 사용되기 때문에각 서버의 시간 동기화에 대해 걱정하게 되는데자주(매 초마다)

새로운 값으로 바뀌고 증가하고 있다는 사실이 중요하고 타임스탬프의 실제 값은 크게 중요하지 않기 때문에

동기화가 꼭 필요하지는 않다.

Machine : ObjectId가 생성된 컴퓨터의 고유 식별자이다이것은 보통 서버 호스트명의 해쉬이다.

(각기 다른 서버들이 충돌하는 ObjectId를 만들지 못하는 것을 보장한다.)

PID : 한 서버에서 동시에 ObjectId를 생성하는 다른 프로세스간의 고유성을 제공하기 위한 ObjectId 생성

프로세스의 pid를 저장한다.

Increment : 일 초 내 단일 프로세스의 고유성을 책임지는 증가 카운터이다.

한 프로세스당 256^3(16,777,216)개의 고유 ObjectId를 생성할 수 있도록 한다.

 

_id 의 자동 생성

문서가 삽입되었을 때 “_id” 키가 없으면 해당 문서에 자동으로 추가된다.

MongoDB 서버에서 처리할 수 있지만 일반적으로 클라이언트 쪽의 드라이버가 처리한다.

 > 작업을 클라이언트쪽으로 넘김으로써 데이터베이스의 확장 부담을 줄일 수 있다.

 > 클라이언트 쪽에서 ObjectId를 생성하기에 드라이버는 insert 메소드에서 입력하는 문서의 생성된 ObjectId

를 반환하거나 문서에 직접 삽입할 수도 있다드라이버가 서버에서 ObjectId를 생성하도록 했다면,

삽입된 문서에 대한 “_id” 값을 구하기 위해서는 별도의 쿼리가 필요할 것이다.

 

 

문서의 생성갱신삭제

1. 문서의 삽입과 저장

db.foo.insert({“bar” : “baz”})

 

일괄 삽입

컬렉션에 여러 문서를 삽입할 때는 일괄 삽입이 보다 빠르다일괄 삽입은 문서의 배열로 전달 할 수 있다.

일괄 삽입은 오버해드를 없애주는 TCP 요청 방식으로메시지별로 드는 헤더 처리 과정을 없애 처리 시간을 단축시켜준다. (한 문서를 데이터베이스에 전송할 때특정 컬렉션에 insert 작업을 수행하도록 데이터베이스에 지시하는 헤더가 앞에 붙는다일괄 삽입을 사용하면 각 문서별로 이러한 헤더를 다시 처리할 필요가 없다.)

하나의 컬렉션에 데이터를 입력할 때만 유용하다.

원본 데이터를 이전하는 경우에는 mongoimport와 같은 커맨드라인 도구를 이용한다.

MongoDB의 현재 버전은 일괄 삽입할 수 있는 데이터의 크기를 16MB로 제한하고 있다.

 

MongDB는 삽입 시 어떤 코드도 실행하지 않기 때문에 인젝션 공격으로부터 안전하다.

기존의 인젝션 공격은 MongoDB에 불가능하며특히 삽입은 더욱 안전하며 그 외의 공격 또한 일반적으로 방어하기 쉽다.

 

2. 문서 삭제

db.users.remove() // users 컬렉션에 있는 모든 문서를 삭제 한다.

mailing.list 컬렉션에서 “opt-out”  true 인 모든 사람을 삭제 하고 싶다면

db.mailing.list.remove({“opt-out” : true})

한번 데이터가 지워지면 영원히 사라진다삭제를 취소하거나 지워진 문서를 복구할 수 있는 방법은 없다.

 

3. 문서 갱신

대대적인 스키마 변경에 유용하다예를 들어 아래 1번 문서를 2번 문서처럼 변경하고자 한다면

1

2

{

“_id” : ObjectId(“4b2b9f67a1631733d917a7a”),

“name” : “kim”,

“friends” : 32,

“enemies” : 2

}

{

“_id” : ObjectId(“4b2b9f67a1631733d917a7a”),

“username” : “kim”,

“relationships” :

   {

       “friends” : 32,

       “enemies” : 2

   }

}

var joe = db.users.findOne({“name” : ”kim”});

joe.relationships = {“friends” : joe.friends, “enemies” : joe.enemies};

joe.username = joe.name;

delete.joe.friends;

delete.joe.enemies;

delete.j.name;

db.users.update({“name” : “joe”}, joe);

 

제한자 사용법

- $inc 제한자

{

  “_id” : ObjectId(“4b2b9f67a1631733d917a7a”),

“url” : “www.pnpsecure.com”,

“pageviews” : 2

}

누군가 페이지에 방문할 때마다, URL로 문서를 찾고 “pageviews” 키의 값을 1 증가하는 “$inc” 제한자를 사용할 수 있다.

db.homepage.update({“url” : “www.pnpsecure.com”}, {“$inc” : {“pageviews” : 1}})

Perl  PHP 프로그래머는 $를 변수의 접두사로 사용하고큰따옴표로 둘러싸인 $를 접두사로 하는 변수를 그 값으로 치환하기 때문에 유의하여 예외처리 해야 한다.

- $set 제한자

키의 값을 설정하며 키가 존재하지 않을 경우 생성한다스키마를 갱신하거나 사용자 정의 키를 추가할 때 편히게 사용할 수 있다.

{

  “_id” : ObjectId(“4b2b9f67a1631733d917a7a”),

“name” : “Kim young hoi,

“sex” : “male”

}

위의 문서에 좋아하는 운동을 추가로 저장해 본다.

db.users.update({“_id” : ObjectId(“4b2b9f67a1631733d917a7a”)}, {“$set” : {“favoritSports” : “basketball”}})

- $unset 제한자

키와 값을 모두 제거할 수 있다.

db.users.update({“name” : “joe”}, {“$unset”, {“favorite book”, 1}})

 

키를 추가하고 변경하고 제거할 때 항상 $제한자를 사용해야 한다.

 

배열 제한자

$push

지정된 키가 이미 존재하면 배열의 끝에 요소를 추가하고그렇지 않으면 새로운 배열을 생성해 추가한다.

db.blog.posts.update({“title” : “A blog post”}, {$push, {“comment” : {“name” : “kim”,

“email” : “yhkim@pnpsecure.com”, “content” : “”good post”}}})

일반적으로 같은 값이 존재하지 않는 경우에만 배열에 값을 추가한다.

이때는 쿼리 문서에 “$ne”를 사용하면 된다.

db.papers.updatE({“authors crited” : {“$ne” : “younghoi”}}, {$push : {“authors crited” : “younghoi”}})

이는 또한 $addToSet 을 통해 가능하다. (중복을 막기 위해)

 

여러 개의 고유 값을 추가하기 위해 $addToSet  $each를 결합해 사용할 수 있다.

db.users.update({“_id” : ObjectId(“4d2d75476cc613d5ee930164”)}, {“$addToSet” : {“email” :

{“$each” : [“kim@pnpsecure.com”, “young@pnpsecure.com”, “hoi@pnpsecure.com”]}}})

$pull

주어진 조건에 일치하는 배열의 요소를 제거하는데 사용한다.

db.list.insert({“todo” : [“dishes”, “laundry”, “dry cleaning”]})

“laundry” 를 제거해 본다.

db.list.update({}, {“$pull” : {“todo” : “laundry”}}) 조건에 일치하는 모든 문서를 제거하게 된다.

 

갱신 입력

있다면 update 하고 없다면 insert 한다.

db.analytics.update({“url” : “/blog”}, {“$inc” : {“visit” : 1}}, true)  true 파라메터가 입력 갱신 여부를 지정한다.

저장 쉘 도우미

save는 존재하지 않을 경우 문서를 삽입하거나 존재할 경우 문서를 갱신할 수 있는 쉘 함수이다.

var x = db.foo.findOne()

x.num = 42

db.foo.save(x)  a 만약 save 를 사용하지 않는다면 db.foo.update({“_id” : x._id}, x, true) 가 되어야 한다.

다중 문서 갱신

기본적으로 갱신은 조건에 일치하는 첫 번째 문서만 갱신한다.

조건과 일치하는 모든 문서를 수정하려면 update 의 네번째 parameter true로 실행한다.

갱신이 어떻게 작동하는지 정확히 할뿐 아니라 기본 작동이 바뀌어도 프로그램은 정상적으로 작동할 수

있도록 다중갱신 parameter를 항상 사용할 것을 권장한다.

갱신한 문서의 반환

getLastError 를 통해 무엇을 갱신하였는지 알수 있으나실제로 갱신한 문서를 반환하지는 않는다.

이를 위해 findAndModify 명령어가 필요하다. findAndModify 는 데이터베이스의 응답을 기다려야 하기 때문에 일반적인 update와는 다르게 약간 느리다.

findAndModify는 아래와 같은 제약사항이 있다.

한 번에 한 문서만 갱신하거나 제거할 수 있다.

갱신입력을 사용할 수 없고 오직 존재하는 문서만 갱신할 수 있다.

 

4. 데이터베이스 중 가장 빠른 쓰기 연산

insert, remove, update 연산은 데이터베이스의 응답을 기다리지 않기 때문에 즉각적으로 처리되는 것 같은 느낌이 든다. (비동기 방식은 아니다.)

이 방법의 장점은 연산 속도가 굉장히 빠르다는 것이다간혹 데이터베이스 서버가 깨지거나 불가피한 상황에서는 잘못되는 경우가 있다.일부 애플리케이션은 이를 감내할 수 있다어떤 애플리케이션에서는(결재 시스템과 같은이러한 방식을 감내할 수 없다.

 

안전 연산

관계형 데이터베이스를 이용한 애플리케이션은 반환 코드를 확인하거나 신경 쓰지 않았음에도 반환 코드를

기다리는 성능 저하를 일으킨다. MongDB는 이런 선택을 사용자에 검긴다.

이 덕분에 로그 메시지나 실시간 지표 데이터 수집 프로그램들은 반환 코드를 기다릴 필요가 없게 된다.

안전 연산은 기본 연산을 수행한 후 바로 getLastError 명령어를 수행해 성공 여부를 확인 한다.

안전 연산을 수행하면 성능이 희생된다.

 

정상적인 오류 처리

실제 배포 시에는 지우더라도개발하는 동안에는 모든 연산을 안전 연산으로 쓰는 것을 권장한다.

드라이버에서는 선택적이지만 쉘은 항상 오류를 확인한다.

 

5. 요청과 연결

MongDB 서버의 각 연결별로 데이터베이스는 요청을 위한 큐를 만들어 클라이언트의 요청을 처리 한다.

하나의 연결에 하나의 큐가 할당된다.

 

 

쿼리하기

1. 찾기 소개

MongoDB에서 find 메소드는 쿼리에 사용한다.

age가 31인 사용자 찾기 : db.users.find({“age” : 31})

age 31이면서 name younghoi 인 모든 사용자 찾기 : db.users.find({“age” : 31, “name” : “younghoi”})

 

반환받을 키 지정하기

db.users.find({}, {“age” : 1, “name” : 1})  a key  age, name, addr, mobile 이 있을 경우 age, name 만 반환한다.

“_id” 는 항상 반환된다.

특정 key 만 제외 시키려면 db.users.find({}, {“age” : 0}) 하면 된다이는 “_id” 의 반환을 제약할수도있다.

 

2. 쿼리조건

비교 연산자

“$lt”, “$lte”, “$gt”, “$gte” 는 각각 <, <=, >, >=에 해당하는 비교 연산자다.

예를 들어 18세 이상에서 25세 이하 사이의 사용자를 찾는 쿼리는 다음과 같다.

db.users.find({“age” : {“$gte” : 18, “$lte” : 25}})

2013 1 1일에 등록한 사람을 찾는 방법은 아래와 같다.

var start = new Date(“01/01/2013”)

db.users.find({“registerd” : {“$lt”, start}})

 

- $or

db.users.find({“$or”, [{“age” : 31}, {“name” : “younghoi”}]})  // age 31이거나 name  younghoi 인 사용자 찾기

 

- $in (나이가 29 혹은 30 혹은 31 인 사용자 찾기)

db.users.find({“age” : {“$in” : [29, 30, 31]}})

“$in” 의 반대는 “$nin” 이다.

 

- $not

“$not”은 특히 정규표현식과 함께 사용해 주어진 패턴과 일치하지 않는 문서를 찾을 때 유용하다.

 

3. 형 특정(Type-Specific) 쿼리

null : null은 스스로 일치하는 것 뿐만 아니라 존재하지 않는” 것과도 일치한다따라서 키가 null 값인 것을

쿼리하면 해당 키를 가지고 있지 않은 모든 문서도 반환된다.

만약 키가 존재하고 값이 null인 것만 쿼리하고자 하면 db.users.find({“z” : null, “$exists” : true})

 

정규 표현식

이름이 Kim 이거나 kim 인 사용자를 모두 찾고자 할 때 db.users.find({“name” : /joe/i})

MongoDB Perl 호환 정규표현식(PCRE) 라이브러리를 사용하며, PCRE에서 쓸 수 있는 모든 문법을 사용 할 수

있다.

 

배열에 쿼리하기

db.food.insert({“fruit” : [“apple”, “banana”, “peach”]})

db.food.find({“fruit”, “banana”});

$all 연산자

배열 내 하나 이상의 요소가 일치하는 배열을 찾으려면 “$all” 을 사용한다.

db.food.insert({“_id” : 1, “fruit” : [“apple”, “banana”, “peach”]})

db.food.insert({“_id” : 2, “fruit” : [“apple”, “kumquat”, “orange”]})

db.food.insert({“_id” : 3, “fruit” : [“cherry”, “banana”, “apple”]})

“apple” 과 “banana”요소를 “$all” 연산자와 함께 써서 해당 문서들을 찾을 수 있다.

db.food.find({“fruit” : {“$all” : [“apple”, “banana”]}})

$size

배열에 쿼리할 때 유용한 조건절은 주어진 크기의 배열을 반환하는 “$size”.

db.food.find({“fruit” : {“$size” : 3}})

$slice

배열 요소의 부분 집합을 반환받을 수 있다.

예를 들어 블로그 게시물의 먼저 달린 댓글 열 개를 반환받고자 한다고 하면

db.blog.post.findOne(criteria, {“comment” : {“$slice” : 10}})

반대로 나중에 달린 댓글 열 개를 반환받고자 한다면

db.blog.post.findOne(criteria, {“comment” : {“$slice” : -10}})

24번째부터 10개를 가져오려면

db.blog.post.findOne(criteria, {“comment” : {“$slice” : [23, 10]}})

특별히 명시하지 않는 한, “$slice”연산자는 문서 내 모든 키를 반환한다이는 명시하지 않은 키를 반환하지

않는 다른 키 명시자들과는 다르다.

 

내장 문서에 쿼리하기

db.blog.find()

{

 “contents” : “….”,

 “comments” : [

           {

             “author” : “joe”,

             “score” : 3,

             “comment” : “nice post”

          },

           {

             “author” : “kim”,

             “score” : 6,

             “comment” : “normal post”

           }

   ]

}

게시물 중 5점 이상인 joe 의 댓글을 찾고자 하면

이때 db.blog.find({“comments.author” : “joe”, “comments.score” : {“$gt”, 5}}) 로 할 수 있는데 이 쿼리는 작동

하지 않는다왜냐하면 댓글의 score 조건과 author 조건은 댓글 배열 내 각기 다른 댓글 문서와 일치할 수 있기

때문이다.

이럴 경우 “$elemMatch” 를 사용한다배열 내에서 하나의 내장 문서를 찾기 위한 조건을 부분적으로 지정할 수

있도록 해준다.

db.blog.find({“comments” : {“$elemMatch” : {“author” : “joe”, “score” : {“$gt” : 5}}}})

“$elemMatch” 는 조건의 그룹핑’ 을 지원한다따라서 내장 문서에 하나 이상의 키에 대해 조건과 일치 여부를

확인할 때만 필요하다.

 

$where 쿼리

“$where” 쿼리는 “$where” 절 내 자바스크립트로 처리한다.

db.food.find({“$where” : function(){

   for(var current in this){

    for(var other in this){

      if(current != other && this[current] == this[other]){

          return true;

      }

    }

  }

  return false;

}})

true를 반환하면 해당 문서는 결과 셋에 포함될 것이고 false 이면 포함되지 않는다.

“$where” 쿼리는 일반 쿼리보다 훨씬 느리기 때문에 반드시 필요한 경우가 아니면 사용하지 말아야 한다.

 

5. 커서

데이터베이스는 커서를 사용하여 find의 결과를 반환한다.

var cursor = db.collection.find();

while(cursor.hasNex()){

  obj = cursor.next();

  // do something..

}

find를 호출할 때 쉘은 데이터베이스에 바로 쿼리하지 않는다.

var cursor = db.foo.find().sort({“x” : 1}).limit(1).skip(10);

이 시점에서 쿼리는 아직 수행되지 않았다.

cursor.hasNext()  이 시점에서야 쿼리가 서버로 전송될 것이다.

쉘은 next hasNext 메소드 호출 시 서버까지 왕복횟수를 줄이기 위해서 한번에 처음 100개 또는 4MB 크기의

결과(둘 중 작은 것)를 가지고 온다.

 

제한건너뛰기정렬

limit, skip, sort 는 쿼리가 데이터베이스에 전송되기 전에 추가해야 한다.

db.users.find().limit(3)

db.users.find().skip(3) // 조건과 맞는 처음 3개의 결과를 건너뛴다.

db.users.find().sort({“username” : 1,  “age” : -1})

sort  value ‘1’ 은 오름차순, ‘-1’ 은 내림차순 이다.

사용자 검색 화면의 페이지 처리는 아래와 같이 간단히 할 수 있다.

var page1 = db.users.find({“firstname” : “kim”}).limit(100).sort({“age” : 1})

var page2 = db.users.find({“firstname” : “kim”}).limit(100).skip(100).sort({“age” : 1})

 

많은 수의 skip 피하기

문서 수가 적을 때는 skip을 사용해도 무리가 되지 않는다하지만 많은 수의 결과에서 skip 은 느릴 수 있다.

(이는 MongoDB 뿐 아니라 거의 모든 데이터베이스에서도 마찬가지다.)

skip을 피하기 위해 일반적으로 문서 자체에 조건을 만들어 두거나또는 바로 전 쿼리의 결과를 가지고 다음

쿼리를 계산할 수도 있다.

var page1 = db.users.find().sort({“date” : -1}).limit(100)

var lastDt = null;

while(page1.hasNext()){

  lastDt = page1.next();

}

var page2 = db.users.find({“date” : {“$gt” : lastDt.date}}).sort({“date” : -1}).limit(100)

페이지 매기기에는 범위 쿼리가 성능이 더 좋다.

문서 랜덤 찾기

 

 

 

색인

MongoDB 의 색인은 일반적인 관계형 데이터베이스의 색인과 동일하게 작동한다.

색인 생성은 ensureIndex 메소드를 이용한다.

db.users.ensureIndex({“username” : 1})

색인의 방향에 따라 1 또는 -1의 값을 가지는 sort 함수에 전달하는 문서와 같은 형식으로 넘길 수 있다.

색인 생성의 단점은 모든 입력갱신삭제에 조금씩 부담을 추가하게 되는 것이다.

따라서 꼭 필요한 최소한의 색인만 생성해야 한다.

때로는 색인을 사용하지 않는 것이 가장 효율적인 대책이다.

 

색인 확장하기

색인을 생성해야 할지 결정할 때는 몇가지 질문을 염두에 둔다.

1. 무엇을 쿼리할 것인가이런 키들 중 일부는 색인이 필요하다.

2. 각 키의 올바른 색인 방향은 무엇인가?

3. 어떻게 확장할 것인가자주 쓰이는 색인을 메모리에 더 올려 둘 수 있도록 색인 내 키를 다르게 정렬할 수

있는가?

 

정렬을 위해 색인하기

컬렉션이 커짐에 따라대규모의 정렬을 위해서도 색인이 필요하다.

색인하지 않은 키에 sort 를 호출하면, MongoDB는 이를 정렬하려고 모든 데이터를 메모리에 올린다.

색인하지 않고 대규모의 정렬을 하는 것에는 한계가 있다컬렉션이 메모리에서 정렬하기에 너무 크면,

MongoDB는 해당 쿼리에 바로 오류를 반환한다.

정렬을 색인하면, MongoDB는 어떤 크기의 데이터라도 메모리의 부족 없이 정렬된 데이터를 꺼내올 수 있다.

 

고유하게 식별되는 색인

색인의 이름은 기본적으로 keyname1_dir1_keyname2_dir2…keynameN_dirN 형태이며, keynameX가 색인의 키이고

dirX가 색인의 방향(1 or -1)이다색인의 이름을 아래와 같이 지정할 수 있다.

db.users.ensureIndex({“a” : 1, “b” : 1, “c” : 1, …. “z” : 1}, {“name” : “alphabet”})

 

고유 색인 (unique key)

db.users.ensureIndex({“username” : 1}, {“unique” : true})

 

중복 제거하기

기존 컬렉션에 고유 색인을 생성할 때 일부 값이 중복될 수 있다이때 하나라도 중복되면 색인 생성에 실패한다.

dropDups 옵션은 처음 발견된 문서를 저장하고 중복된 값을 가지는 다음 문서는 모두 제거한다.

db.users.ensureIndex({“username” : 1}, {“unique” : true, “dropDups” : true})

 

explain  hint 사용하기

explain은 쿼리에서 사용하는 색인에 대한 정보(만약 있다면)와 시간에 대한 통계와 살펴본 문서 수를 반환한다.

db.users.find().explain()

{

 “cursor” : “BasicCursor”,

 “indexBounds” : [ ],

 “nscanned” : 64,                     // 데이터베이스가 훓은 문서의 수

 “nscannedObjects” : 64,

 “n” : 64,                            // 반환 받은 문서의 수

 “millis” : 0,                          // 쿼리 수행 시간(1/1000)

 “allPlans” : [

   {

      “cursor” : “BasicCursor”,

      “indexBounds” : [ ]

   }

  ]

}

Mong가 쿼리에서 원하는 것과 다른 색인을 사용하는 것을 발견하면, hint 메소드를 사용해 특정 색인을 강제로

사용하도록 할 수 있다.

db.users.find({“age” : 14, “username” : /.*/}).hint({“username” : 1, “age” : 1})

 

색인관리

색인에 대한 메타정보는 각 데이터베이스의 system.indexes 컬렉션에 저장된다.

 

색인변경

색인을 생성하는 일은 오랜 시간이 걸리고 자원을 많이 잡아 먹는다. “background” 옵션을 사용하면

데이터베이스가 다른 요청을 처리하는 동안 뒷단에서 색인을 생성한다.

db.users.ensureIndex({“username” : 1}, {“background” : true})

이 옵션을 사용하지 않으면데이터베이스는 색인을 구축하는 동안 다른 모든 요청을 차단할 것이다.

dropIndexes 명령어와 색인 이름을 이용해 색인을 제거할 수 있다.

db.runCommand({“dropIndexes” : “users”, “index” : “alphabet”})

db.runCommand({“dropIndexes” : “users”, “index” : “*”}) // 컬렉션 내 모든 색인을 제거한다.

 

공간 정보 색인

MongoDB는 송간 정보 색인이라는좌표 평면 쿼리에 대한 특별한 형태의 색인을 제공한다. (모바일에서 사용)

ex) 현재 위치의 위도와 경도로 가장 가까운 커피숍을 찾는다

공간 정보 색인은 ensureIndex  1 또는 -1 의 값 대신 “2d” 를 전달하여 생성한다.

db.star.trek.ensureIndex({“light-years” : “2d”}, {“min” : -1000, “max” : 1000})

“$near”, “$within”, “$box”, “$center” 등의 여러 제한자가 있으나 이는 생략한다.,

 

 

 

집계

count

db.users.count()

dbb.users.count({“x” : 1})

 

distinct

distinct 명령어는 주어진 키의 고유한 값을 찾는다컬렉션과 키를 반드시 지정해야 한다.

dbb.runCommand({“distinct” : “users”,  “key” : “age” })

 

group (sql  group by)

db.runCommand({“group” : {

   “ns” : “stocks”,         // 어떤 컬렉션에서 group을 수행할지 결정

   “key” : “day”,          // 컬렉션 내에서 문서를 묶을 키를 지정

   “initial” : {“time” : 0},   // 처음으로 리듀스 함수가 호출될 때이것을 초기화 문서로 넘긴다.

   “$” : function(doc, prev) {

      if(doc.time > prev.time) {

          prev.price = doc.price;

          prev.time = doc.time;

      }

  }}})

 

맵리듀스

맵리듀스는 집계 도구의 막강한 연산능력이 있는 분산 처리 툴과 같은 존재다.

count, distinct, group 명령으로 할 수 있는 그 이상을 맵리듀스 하나로 할 수 있다다수의 서버에 걸쳐서 쉽게

병렬화 할 수 있는 집계 방법이다.

맵리듀스의 단점은 속도이다. ‘실시간으로는 사용하지 않는 것이 좋다.

맵리듀스를 뒤에서 돌려 컬렉션을 만들고해당 컬렉션에 실시간으로 쿼리할 수 있다.

믿을 수 없을 만큼 유용하고 강력하지만 그만큼 복잡한 도구이다.

 

맵리듀스는 몇 단계를 걸쳐 수행된다맵단계부터 시작하는데이는 컬렉션의 모든 문서에 대해 연산을 맵핑한다.

연산은 아무것도 안함’ 이나 이 키들은 x 값으로 출력’ 둘중 하나가 될수 있다.

그 다음 키들을 묶고 각 키별로 출력한 값들의 목록을 생성하는 셔플단계를 거친다.

리듀스는 이 목록을 받아 하나의 요소로 줄인다.

이 요소는 각 키가 하나의 값즉 결과를 가질 때 까지 다시 셔플 단계로 전달된다.

 

ex) 컬렉션에서 모든 키 찾기

map = function(){

  for(var key in this){

    emit(key, {count : 1});

}

}

 

reduce = function(key, emits){

  total = 0;

  for(var i in emits){

    total += emits[i].count;

  }

return {“count” : total};

}

 

mr = db.runCommand({“mapreduce” : “users”, “map” : map, “reduce” : reduce})

{

  “result” : “tmp.mr”MapReduce_1266787811_1”,

  “timeMillis” : 12,

  “count” : {

     “input” : 6,    // 맵 함수에 보내진 문서의 수

     “emit” : 14,    // 맵 함수에서 호출한 emit 의 수

     “output” : 5   // 결과 컬렉션에 생성된 문서의 수

  },

  “ok” : true

}

db[mr.result].find()

{“_id” : “_id”, “value” : {“count” : 6}}

{“_id” : “a”, “value” : {“count” : 4}}

{“_id” : “b”, “value” : {“count” : 2}}

{“_id” : “x”, “value” : {“count” : 1}}

{“_id” : “y”, “value” : {“count” : 1}}

 

 

 

 

 

 

고급 기능

데이터베이스 명령어

명령어는 “create, read, update, delete”에 맞추어 넣을 수 없는 모든 기능을 구현한다.

앞에서 update로 영향받은 문서의 수를 알기위해 getLastError 명령어를 사용했다.

db.users.update({x : 1}, {“$inc” : {x : 1}}, false, true}

db.runCommand({getLastError : 1})

{

  “err” : null,

  “updateExisting” : true,

  “n” : 5,

  “errmsg” : “ns not found”,  // 수행 결과가 실패일 경우 오류 메시지

  “ok” : false   // 수행 결과 성공 여부

}

자주 사용되는 MongoDB 명령어 목록이다.

buildinfo

{“buildinfo” : 1} 

// 관리자 전용 명령어로 MongoDB 서버의 버전과 OS 정보를 반환한다.

collStats

{“collStats” : collection}

// 주어진 collection 의 데이터 크기할당 된 저장 공간의 용량색인의 크기 통계를 제공

distinct

{“distinct” : collection,  “key” : key, “query” : query}

// collection 내에서 query 와 조건에 맞는 문서 내 key 의 고유한 값 목록을 얻는다.

dropDatabase

{“dropDatabase” : 1}

// 현재 데이터베이스의 모든 데이터를 삭제 한다.

dropIndexes

{“dropIndexes” : collection, “index” : name}

collection에서 name 으로 지정한 이름의 색인을 삭제 한다. name 이 “*” 이면 모든 색인을 삭제한다.

findAndModify

….

getLastError

{“getLastError” : 1[, “w” : w[, “wtimeout” : timeout]]}

// 현재 연결에서 마지막으로 수행한 작업의 오류나 상태 정보를 확인한다.

isMaster

{“isMaster” : 1}

// 현재 서버가 마스터인지 슬레이브인지 확인한다.

listCommands

{“listCommands” : 1}

// 현재 서버에서 사용 가능한 데이터베이스 명령어와 각 명령어에 대한 정보를 반환한다.

listDatabase

{“listDatabases” : 1}

// 관리자 전용 명령어로 현재 서버의 모든 데이터베이스 목록을 반환한다.

 

ping

{“ping” : 1}

// 서버가 살아 있는지 확인한다서버가 잠금 상태에 걸려 있어도 바로 결과를 반환한다.

renameCollection

{“renameCollection” : a, “to” : b}

// 컬렉션 a 의 이름을 b 로 변경한다.

repaireDatabase

{“repaireDatabase” : 1}

// 현재 데이터베이스를 복구하고 줄이며 이 작업은 꽤 오랜 시간이 걸린다.

serverStatus

{“serverStatus” : 1}

// 현재 서버의 관리적 통계를 얻는다.

 

제한 컬렉션

MongoDB는 사용 전에 생성하며 크기를 제한하는 제한 컬렉션을 제공한다.

제한 컬렉션은 공간이 부족해 지면 가장 오래된 문서가 삭제 된다.

제한 컬렉션은 문서를 삭제할 수 없고(자동 삭제 제외), 갱신도 허용하지 않으며 색인이 없다.

제한 컬렉션은 입력 속도가 매우 빠르다.  입력 순서대로 뽑아내는 쿼리가 매우 빠르다.

이런 특징을 가진 제한 컬렉션은 로깅과 같은 사용 사례에 매우 이상적이다.

일반 컬렉션과 다르게제한 컬렉션은 사용하기 전에 명시적으로 생성해야 한다.

db.createCollection(“my_collection”, {capped : true, size : 100000, max : 100});

 

 

GridFS : 파일 저장하기

GridFS MongoDB에 대용량 이진파일을 저장하기 위한 메커니즘이다.

- GridFS를 사용하면 아키텍처 스택을 단순화할 수 있다이미 MongoDB를 사용한다면 또 다른 저장소 아키텍쳐보다는 GridFS를 사용하면 된다.

- GridFS MongoDB를 위해 설정한 복제나 자동 조각화를 이용할 수 있어파일 저장소를 위한 장애 복구와 분산확장이 매우 쉽다.

- GridFS는 사용자가 올린 파일을 저장할 때 특정 파일시스템이 가지는 문제를 피할 수 있다예를 들어 GridFS는 같은 디렉터리에 대량의 파일을 저장해도 문제가 발생하지 않는다.

- MongoDB 2GB 청크로 데이터 파일을 할당하기 때문에 GridFS 를 이용하면 디스크 지역성을 얻을 수 있다.

 

서버 측 스크립트

db.eval MongoDB 서버에서 임의의 자바스크립트를 실행할 수 있게 해주는 함수다.

db.eval(“return 1;”)

dbb.eval(“function(x, y, z) { return x + y + z; }”, [num1, num2, num3])

저장 자바스크립트

MongoDB는 각 데이터베이스에 자바스크립트 변수를 저장할 수 있는 system.js 라고 하는 특수한 컬렉션을

가지고 있다.

db.system.js.insert({“_id” : “x”,  “value” : 1})

db.system.js.insert({“_id” : “y”,  “value” : 2})

db.system.js.insert({“_id” : “z”,  “value” : 3})

dbb.eval(“return x + y + z;”)  a 6

저장 자바스크립트를 사용할 때 단점은 소스 코드 관리 범위 밖에 코드의 일부가 존재하게 되며클라이언트가

보낸 자바스크립트를 이해하기 어렵게 할 수 있다.

저장 자바스크립트는 하나의 자바스크립트 함수를 여러 코드에서 사용할 때 쓰면 가장 좋다중앙에서 이러한

함수를 유지하는 것은 변경이 필요한 경우에도 여러 곳에서 갱신하지 않아도 된다는 의미다저장 자바스크립트

는 또한 자바스크립트 코드가 길고 자주 실행되는 경우에도 한번 저장하고 나면 네트워크 전송 시간을 줄일 수

있기 때문에 유용하게 사용할 수 있다.

보안

MongoDB의 보안 관점에서 조심해야 하는 몇 안되는 경우 중 하나는 자바스크립트를 실행할 때이다.

잘못하면 관계형 데이터베이스처럼 인젝션 공격에 취약해 진다이를 막기 위해 유효 영역을 통해 변수를 전달해

야 한다.

 

데이터베이스 참조(DBRef)

DBRef MongoDB의 다른 내장 문서와 똑 같은 내장 문서이다.

하지만 DBRef는 반드시 필요로 하는 특정한 키를 가지고 있다.

{“$ref” : collection, “$id” : id_value,  “$db” : database}

 

 

관리

MongoDB mongod 실행파일을 사용하는 일반적인 커맨드라인 프로그램이다.

MongDB는 관리 인터페이스와 모니터링 기능을 기본으로 제공하며 제 3의 모니터링 패키지와 쉽게 연동할

수 있다.

MongDB는 읽기 전용 사용자와 세분화된 관리자 접근을 포함하는 기본적인 데이터베이스 수준의 인증을

지원한다.

여러 백업 도구를 제공하며 고려 사항에 따라 선택할 수 있다.

 

커맨드라인에서 시작하기

MongoDB 서버는 mongod 실행 파일로 시작한다.

자주 사용되는 옵션은 아래와 같다.

--dbpath

 데이터 디렉토리로 사용할 경로를 지정한다기본값은 /data/db  (윈도우에서는 C:\data\db\)

 하나으 ㅣ서버 내 mongod 프로세스들은 각기 별도의 데이터 디렉토리를 필요로 한다.

--port

 기본적으노 27017번 포트를 사용하며하나 이상으 ㅣmongod 프로세스를 시작하고자 할 때는 각각 다른 포트

 번호를 지정해야 한다.

--fork

 서버 프로세스를 포크시켜서 데몬으로 작동시킨다.

--logpath

 모든 출력을 커맨드라인에 표시하지 않고 지정된 파일로 출력한다만약 파일이 존재하지 않으면 새로 생성하며,

 이미 파일이 존재하면 덮어쓰기를 한다이때 해당 디렉토리에 대한 쓰기 권한이 있어야 한다.

 예전 로그를 보존하고 싶다면, --logpath와 함께 –logappend 옵션을 사용한다.

--config

 커맨드라인에 지정하지 않은 추가 옵션을 위한 설정파일을 사용한다.

 mongod --config ~./mongodb.conf

 

MongoDB 중지하기

포어그라운드에서 구동하고 있다면 Ctrl+C 로 종료 아니면 kill(SIGTERM) PID 또는 kill -2(SIGINT) PID를 이용하여

종료하거나 db.shutdownServer()를 이용한다구동중인 MongoDB 서버에 SIGKILL(kill -9)를 보내서는 안된다. (다시 시작하기 전에 복구 작업을 거쳐야 한다.)

현재의 작업이나 파일 사전 할당이 종료될 때까지 대기했다가 모든 연결을 닫고메모리에 남아있는 데이터를 디스크에 쓴 후 종료된다.

 

모니터링

기본적으로 mongod를 시작하면 1,000을 더한 포트에서 연결을 대기하는 아주 단순한 HTTP 서버가 함께 시작된다이 서버는 MongoDB서버에 대한 기본 정보를 HTTP 인터페이스를 통해 제공한다.

이 기능을 끄려면 –nohttpinterface 옵션을 사용하면 된다.

 

serverStatus

db.runCommand({“serverStatus” : 1})

serverStatus 명령어를 실행하여 MongoDB에 대해서 가장 기본적인 통계 정보를 얻을 수 있다

serverStatus는 서버의 상태와 성능을 모니터링하기에 강력하지만 사용자가 쉽게 사용할 수 있는 도구는 아니다.

 

mongostat

mongostat serverStatus의 결과 중 몇 가지 중요한 정보들을 출력한다.

 

 

보안과 인증

MongoDB는 비록 세세한 수준의 권한 스키마를 가지고 있지는 않지만연결당 인증 기능을 지원한다.

db.adduser(“root”, “abcd”)

db.adduser(“kim”, “123”, true) // read-only : true

 

인증 과정 방식

데이터베이스의 사용자들은 system.users 컬렉션에 문서로 저장된다.

{“user” : username, “readOnly” : true, “pwd” : password hash} // password hash username password에 기반한 해쉬이다.

 

기타 보안 관련 사항

MongoDB의 와이어 프로토콜은 인증을 사용할 때도 암호화하지 않는다암호화가 필요하다면 SSH터널링이나다른 비슷한 매커니즘을 사용하여 서버와 클라이언트 사이의 통신을 암호화하는 방법을 고려해보자.

MongoDB는 항상 방화벽 뒤에 서버를 두거나특정 애플리케이션 서버에서만 접속하게 하자.

( mongod –bindip localhost )

 

 

 

백업과 복구

MongoDB가 운영 중일 때 파일을 복사하는 작업은 안전하지 않기 때문에안전한 백업을 위한 한가지 방법은

MongoDB 서버를 멈춘 후 데이터 디렉토리를 복사하는 것이다.

서버를 중지하고 백업하면 효율적이고 안전하긴 하지만이상적인 방법은 아니다.

mongodump 유틸리티를 사용하면 MongoDB를 운영하는 중에도 백업을 받을 수 있다.

MongoDB는 백업한 데이터를 복원하는 mongostore를 제공한다.

 

fsync와 락

mongodump mongostore MongoDB 서버의 중지 없이 백업과 복원을 할 수 있게 되었지만특정 시점의 데이터를 구할 수는 없다. MongoDB fsync 명령어는 운영 중인 MongoDB 서버의 데이터 오염의 위험 없이 온전하게 복사할 수 있게 해준다.

fsync 명령어는 MongoDB 서버에 디스크에 쓰기가 지연되어 있는 모든 데이터를 강제로 쓰도록 한다.

 

 

 

 

복제

마스터-슬레이브 복제

마스터-슬레이브 복제는 MongoDB가 제공하는 가장 일반적인 복제 방법이다.

이 방법은 매우 유연해서 백업이나장애 시 요청을 넘기거나읽기 분산 등에 사용 할 수 있다.

 

$ mkdir –p ~/dbs/master

$ ./mongod --dbpath ~/dbs/master --port 10000 --master

$ mkdir –p ~/dbs/slave

$ ./mongod --dbpath ~/dbs/slave --port 10001 --slave --source localhost:10000

 

마스터-슬레이브 복제와 같이 사용할 수 있는 몇 가지 유용한 옵션이 있다.

--only : 슬레이브 노드에서 단 하나의 복제할 데이터베이스만 지정한다. (기본값은 모든 데이터베이스를 복제)

--slavedelay : 슬레이브 노드에서 마스터 노드로부터 반영할 동작을 초 단위로 지연한다.

             사용자 실수로 중요한 문서를 삭제하거나잘못된 데이터를 입력하는 경우에 유용한 지연된

슬레이브를 쉽게 설정할 수 있다.

--fastsync : 마스터 노드의 스냅샷으로 슬레이브를 기동한다빠르게 슬레이브 노드를 기동할 수 있다.

--autoresync : 슬레이브가 마스터와 동기화에 실패한 경우 자동으로 전체 재동기화를 수행한다.

--oplogSize : 마스터의 oplog 사이즈(MB 단위)를 지정한다.

 

소스 추가 및 제거

--source 옵션으로 마스터를 지정할 수 있지만 쉘에서도 같은 작업을 할 수 있다.

db.sources.insert({“host” : “localhost:27017”})

실제 서비스 환경에서 슬레이브의 설정을 변경한다고 가정하면 insert  remove를 사용해서 소스를 변경할 수 있다소스는 일반 문서처럼 다룰 수 있기 때문에 유연하게 슬레이브를 관리할 수 있다.

 

레플리카 셋

레플리카 셋은 기본적으로 자동 장애 넘김 기능이 있는 마스터-슬레이브 클러스터이다.

마스터-슬레이브 클러스터와 레플리카 셋의 가장 큰 차이점은 레플리카 셋은 여러 마스터 노드를 가진다는 점이다레플리카 셋은 모든 것이 자동화되어 있다우선 스스로 슬레이브를 마스터로 바꾸고 일관성을 유지하는 등 많은 관리 작업을 알아서 처리한다마스터 노드에 장애가 발생할 경우 다른 노드로 요청을 넘겨주어 레플리카 셋은 개발자 관점에서 사용 편의성을 제공한다.

 

 

작동 원리

MongoDB의 복제 설정은 언제나 적어도 두 대의 서버 또는 노드로 구성된다.

하나는 마스터로 클라이언트의 일반적인 요청을 처리한다.

다른 하나(또는 셋은)는 슬레이브이고 마스터에 저장된 데이터를 복제하는 역할을 한다.

슬레이브는 주기적으로 마스터에 새로운 연산 기록을 요청하고 데이터의 사본에 그대로 수행한다.

마스터 노드가 수행한 연산을 똑같이 수행함으로써 슬레이브는 마스터 데이터와 같은 최신 데이터를 유지한다.

 

마스터가 수행한 연산 기록을 oplog(operation log) 한다

oplog local이라는 특수한 데이터베이스의 oplog.$main 컬렉션 내에 저장한다.

oplog의 각 문서는 마스터에서 수행한 각 연산들을 나타낸다.

oplog는 데이터베이스의 상태를 변경하는 연산만 저장한다. (select query oplog에 저장되지 않는다.)

 

 

 

샤딩

MongoDB가 분산 확장하는 방식이다.

샤딩으로 애플리케이션에 영향을 주지 않고 증가하는 부하와 데이터를 처리하기 위해 장비를 추가할 수 있다.

MongoDB는 수동 샤딩의 골칫거리를 해결해주는 자동 샤딩을 제공한다.

클러스터가 데이터를 분할하고 자동으로 재조정한다.

MongoDB 자동샤딩의 기본 역할은 컬렉션을 작은 청크 단위로 쪼개는 것이다.

샤드들의 앞단에 mongos라는 라우팅 프로세스가 모든 데이터의 위치를 알고 있기 때문에 애플리케이션 관점에서는 일반 mongod에 연결한 것으로 인식한다.

언제 샤딩을 사용해야 하는가?

현재 장비에 디스크 공간이 모자랄 때

단일 mongod가 처리할 때보다 빠르게 데이터를 쓰고 싶을 때

성능 향상을 위해 더 많은 데이터를 메모리에 올려 두고 싶을 때

일반적으로 비샤딩 방식 설정으로 시작하고 필요 시 샤딩 방식으로 변환한다.

 

샤딩을 설정할 때컬렉션에서 키를 선택하고 그 키의 값을 데이터를 분할 하는데 사용한다이 키를 샤드 키라고 한다예를들어 “name”을 샤드 키로 선택하면 어떤 샤드는 “name”  A~F, 다음 샤드는 G~P, 마지막 샤드는 Q~Z까지의 문서를 가질 수 있다. (네트워크 트래픽데이터양의 균형은 MongoDB가 알아서 처리 한다.)

샤드 키가 무작위든꾸준히 증가하든변화가 조금이라도 있는 갑의 키를 선택하는 것이 중요하다.

사실 많은 경우에 샤드 키는 가장 자주 쓰는 인덱스이다.

상승하는 값을 갖는 필드는 시스템의 상황을 완벽히 이해하고 있지 않다면 샤드키로 사용하지 말아야 한다.

 

샤딩은 기본적으로 각기 다른 세 가지 구성 요소가 함께 작동한다.

샤드

샤드는 컬렉션 데이터의 부분 집합을 보유하는 컨테이너이다. (그냥 평범한 mongod 인스턴스(또는 복제 셋).

- mongos

라우터 프로세스이며 요청을 라우팅하고 응답을 모은다.

사용자와 클러스터 간의 접점이다샤드의 복잡한 내부 모습을 감추고단일 서버와도 같은 깔끔한 인터페이스를 사용자에게 제공한다.

설정 서버

어떤 데이터가 어떤 샤드에 있는지클러스터 설정을 저장한다. mongos는 어떤 데이터도 영구 저장하지 않기 때문에샤드 설정을 받기 위한 곳이 필요하다. mongos는 설정 서버에서 이 데이터를 동기화 한다.

 

 

샤드 정보는 mongos 프로세스 연결된 어떤 연결로도 접근할 수 있는 config 데이터베이스에 대부분 저장된다.

 

 

 

 

MongoDB 내부

BSON

문서 MongoDB 통신에 광범위하게 사용되고 있기 때문에 모든 드라이버도구프로세스에 의해 공유된 문서의 표현식이 필요했다그 표현을 Binary-JSON , BSON 이라고 한다.

BSON은 바이트 문자열로 MongoDB 문서를 표현하는 간단한 바이너리 형식이다.

BSON 포맷은 세 개의 주된 목표를 갖는다.

효율

최악의 BSON은 최상의 JSON보다 약간 비효율적일 뿐이며 일반적으로는 훨씬 효율적이다.

트래버스 기능

문자열의 끝을 의미하는 터미네이터에 의지하지 않고문자열 값에 길이를 접두사로 붙인다.

이 트래버시 기능은 MongoDB 서버가 문서 내성을 필요로 할 때 유용하다.

 

와이어 프로토콜

드라이버는 가벼운 TCP/IP 와이어프로토콜을 사용해서 MongoDB에 엑세스 한다.

프로토콜은 기본적으로 살짝 BSON 데이터를 덮는 형태로 구성된다.

예를 들어 insert 메시지는 20byte header data(어느 서버에 insert를 수행하는지와 메시지 길이)와 삽입될 BSON 문서 리스트와 컬렉션 이름으로 구성된다.

 

데이터 파일

기본적으로 /data/db/로 설정되어 있는 MongoDB 데이터 디렉터리 안에 각 데이터베이스로 분리된 파일들이 있다각 데이터베이스는 하나의 .ns 파일과 단조롭게 증가하는 숫자 확장자의 데이터 파일 몇 개를 갖는다.

 

네임스페이스와 익스텐트

데이터파일 내에서 각 데이터베이스는 네임스페이스로 구성되고데이터의 구체적인 데이터형을 각각 저장한다.

각 컬렉션의 문서는 색인과 같이 그들만의 네임스페이스를 갖는다.

네임스페이스의 메타데이터는 데이터베이스의 .ns 파일에 저장된다.

각 네임스페이스에 대한 데이터는 익스텐트라는 데이터 파일의 섹션으로 디스크에 그룹화 된다.

 

메모리 맵드 스토리지 엔진

MongoDB에 기본 스토리지 엔진은 메모리 맵드 엔진이다.

서버가 가동되면 모든 데이터 파일을 메모리 맵에 올린다.

데이터 페이지 I/O 와 디스크에 쓰는 플러시는 운영체제가 관리한다.

이 스토리지 엔진의 몇가지 중요한 속성은 다음과 같다.

 . 메모리 관리를 위한 MongoDB의 코드는 대부분 운영체제로 떠넘겼기 때문에 매우 심플하고 깔끔하다.

 . MongoDB 서버 프로세스의 가상 크기는 매우 크고자주 전체 데이터의 크기를 초과한다이는 운영체제가

알아서 메모리 내 일정량의 데이터를 유지하기 때문에 무시해도 된다.

 . MongoDB는 단일-서버의 내구성을 위한 로그 선행 기입을 불가능하게 만드는 디스크 쓰기 명령을 제어할

수 없다. MongoDB의 단일-서버 내구성을 보장할 수 있는 차세대 저장소 엔진이 현재 개발 중이다.

. 32bit MongoDB 서버는 mongod당 약 2GB로 제한된다



728x90
반응형
LIST

'독서' 카테고리의 다른 글

펄떡이는 물고기처럼 - 2013.03.25  (0) 2013.03.25
조선 왕을 말하다 - 2013.03.19  (0) 2013.03.19
야구감독 - 2013.02.26  (0) 2013.02.26
카네기 행복론 - 2013.01.28  (0) 2013.01.28
마케팅 전쟁 - 2012.11.30  (0) 2012.11.30