■ MongoDB

[MongoDB] 몽고디비 인덱싱에 대하여..

인덱싱은 개념은 쉽지만 막상 대규모에 적용하려면 상당히 까다로운 문제에 직면하게 된다.

그중 몽고디비에서 아주 사소하지만 반드시 알아둘 것들을 정리해 보았다.

1. 몽고디비에서 부정(negation)에 대한 쿼리는 되도록 사용하지 말자.

부정에 대한 쿼리는 일반적으로 잘 작동하지 못할 수 있다. 작동을 잘 못한단 의미는 잘할수도 있고 아닐수도 있다는 의미다.

좀 더 깊이 가보자. 예를 들면

{
  nick: {
    $ne: /^hwa/
  }
}

다음과 같은 쿼리가 있다고 했을경우,  hwa로 시작되지 않는 문자열을 위해 모든 쿼리를 검색한다.

이는 hwa로 시작되는 문자열을 찾는 것과는 다르게 매우 비효율 적이다.

세세한거 따지기 싫다면 왠만하면 부정은 쿼리에 사용하지 말자. (부정의 반대는 긍정이므로 반드시 대칭되는 쿼리가 있을것이다.)

 

2. 위에서 /^hwa/ 라는 쿼리를 썼다. 정규식은 일반적으로 인덱싱이 되지 않는다. 하지만 접두어(prefix)의 경우 인덱싱이 가능하다.

 

3.  복합인덱스 순서에서 범위필드에 인덱싱은 항상 마지막으로 작성한다. 범위필드는 마지막에 해당하는 모든 쿼리를 찾고 최종적으로 제한하는 역할을 한다.

 

4. 몽고디비에서는 하나의 쿼리당 하나의 인덱스만을 사용할 수 있다. 하지만 OR쿼리는 $or: [] 안에 있는 배열의 개수만큼 인덱스를 이용하여 쿼리를 수행한다. 당연한 말이겠지만 여러개의 검색된 n개의 도큐먼트 집합이 있다는 의미이며, 몽고디비는 이 도큐먼트 집합들을 합치는데 또다른 비용을 소모한다. 가능하면 $in으로 가자.

 

5. 다음과 같은 내장된 도큐먼트에 대한 인덱스를 수행할 경우,

ensureIndex({'user.nick': 1});

반드시 쿼리문 작성 또한

find({'user.nick': 'hwarang'})

위 처럼 되야한다. 만약

find({user:{nick:'hwarang'}})

다음과 같다면 인덱스는 user에 대한 인덱스를 수행할 것이다.

 

6. 배열에 대한 인덱스는 다중키 인덱스라고 하는데, 이는 배열의 모든 요소에 인덱싱을 하는 것이다. 따라서 대체로 일반 인덱스보다 비용이 많이 든다. 또한 두개이상의 배열을 한번에 인덱싱 할수 없다. 예를들면

ensureIndex({friends: 1, family: 1});
find({friends:['1', '2'], family:['0', '1']};

다음과 같은 상황에서 friends와 family 두개의 키로 인덱스를 생성했는데 두개 모두 배열이다. 즉 두개이상의 배열을 한번에 인덱싱할 수없다는 원칙에 의거하여 에러가 날 것이다. 왜 안되는지에 대한 이유는, 다중키 인덱스는 각 요소마다 인덱싱을 한다고 했는데 2개의 배열은 n*m개 3개의 배열은 n*m*s개의 인덱스가 생성될 것이기 때문이다. (이는 심하게 비용낭비이다.)

 

7.  인덱싱할 필드는 카디널리티가 낮은것을 선택하자.  이유는 높은 카디널리티를 갖고 있는 필드는 인덱싱의 위력을 발휘하지 못한다. 낮은 카디널리티일수록 인덱싱의 진가가 발휘된다. (카디널리티는 한 필드의 중복성의 정도를 나타낸다. 만약 필드가 enum타입을 갖고 있다면 낮은 카디널리티가 된다. 가장 높은 카디널리티는 unique key (pk)가 된다.)

Standard
■ AWS, ■ Linux, ■ MongoDB, ■ Node.js

[AWS] 아마존 웹 서비스에 Dokku (Mini Heroku)를 배포해보자! pt.2 (1)

이제 Dokku를 설치했으니 실제 배포를 해보겠습니다.

여기선 node.js를 배포해보도록 하겠습니다. express 나 기타 node.js server를 생성합니다.

이후 터미널을 통해 해당 app.js가 있는 폴더로 들어옵니다. 그리고 아래와 같이 git를 이용해서 remote를 추가시켜줍니다.

여기서 remote의 의미는 해당 git 데이터들을 push할때 올릴 remote server를 의미합니다.

git remote add dokku dokku@<도쿠_서버_주소>:<앱_이름>

이제 git를 통해 업로드를 해보도록 하겠습니다.

 

12

 

 

이제 업로드가 된줄..알았지만 Invalid namespace name (app) 이라는 메세지와 함께 업로드가 거부됩니다. 이유는 dokku의 진짜 실체인 docker버전 때문입니다. docker버전이 업데이트 되면서 최소 4자 이상, 30자 이내의 이름만 사용할수 있게 되기 때문에 app이란 container를 만들수가 없게 됩니다.

 

https://github.com/progrium/dokku/issues/533

해당 경로로 들어가게 되면 이러한 이슈에 대한 해결책이 나와있습니다. 여기서는 docker를 다운그래이드 하도록 하겠습니다.

sudo apt-get remove --purge lxc-docker
sudo apt-get install lxc-docker-0.9.0

위 두명령어를 통해 현재 버전의 docker를 지우고 0.9.0버전의 docker를 설치합니다.

다음으로 아까 실패했던 배포를 다시 시도해 보도록 하겠습니다.

13

 

보는 바와 같이 업로드후 디플로이까지 성공적으로 마쳤습니다.

이제 커맨드보단 우리가 보기 편한 SourceTree를 통해 GUI git를 이용해 보도록 하겠습니다.

위 이미지를 보면 dokku@rankting.com:test 와같은 이름으로 git의 주소가나와있습니다.

해당 주소를 통해 소스트리에서 clone시키면 됩니다.

 

14

이제 기존의 폴더 대신 소스트리에 연결된 워킹카피를 이용하시면됩니다.

다시 ubuntu서버로 돌아와서 다음과 같이 입력해서 실제 container가 잘만들어지고 실행되는지 확인해봅니다.

15

 

보면 49153 -> 5000 으로 리다이렉트 되고 있습니다.

즉 실제 test의 node어플리케이션은 49153포트를 가진다는 것을 알수 있습니다. (맨 앞에는  nginx가 80포트로 자리잡고 해당 서브도메인에 맞도록 라디이렉트 시켜주고 있습니다. 즉 프록시의 역할을 하고 있습니다.)

이제 접속을 시도해 보지만 접속이 되지 않습니다. (현재 노드앱은 자체 mongodb, redis서버의 주소로 접속을 시도하고 있는 앱입니다.)

16

 

따라서 help를 확인해서 logs라는 명령어를 알아낼수 있습니다.

이제 log를 확인해서 어떤 문제가 있는지 보도록 하겠습니다.

 

17

역시나 몽고디비 접속이 되지않고 있습니다.

다음 포스팅에선 dokku plugin을 통해 mongodb와 redis를 연결시켜 보도록 하겠습니다.

Standard
■ MongoDB

[몽고디비] MongoDB Index 노하우를 알아보자!

> db.comments.find( { timestamp: { $gte: 2, $lte: 4 } } ).explain()
{
    "cursor" : "BtreeCursor timestamp_1",
    "n" : 3,
    "nscannedObjects" : 3,
    "nscanned" : 3,
    "scanAndOrder" : false
}

nscanned : 몽고디비가 스캔했던 인덱스 키의 개수를 의미.

nscannedObjects : 최종 도큐먼트를 얻기위해 스캔했던 도큐먼트의 개수를 의미. 또한 리턴된 모든 최소 도큐먼트의 개수를 포함한다.

n : 리턴된 최종 결과 도큐먼트의 개수. 항상 다음과 같은 부등식이 존재한다.
nscanned >= nscannedObjects >= n 이것을 아래와 같은 등식으로 바꾸면 최적의 쿼리가 된다.
nscanned = nscannedObjects = n 일반적으로 범위 $gt, $lt등을 갖고 find해야 할 쿼리에 대해서 해당 필드가 더 뒤에 와야한다.
질의 시 만약 sorting 기능을 활용했다면, scanAndOrder 필드를 살펴봐야한다.
비록 nscanned = nscannedObjects = n 라는 등식을 만족했더라도, scanAndOrder필드가 true라면 다시한번 최적화를 해야한다.
이유는 scanAndOrder필드가 true일땐 모든 도큐먼트를 메모리로 로드해서 다시 정렬했다는 의미가 된다. (CPU와 RAM이 busy 상태가 된다.)

결론은,
1. 중복이 많은 필드
2. 정렬이 필요한 필드
3. 범위로 질의할 필드 순으로 인덱스를 만들면된다. 여기서 중요한점은 a-b의 복합인덱스 일경우 a에 관한 인덱스를 만들필요가 없다. (어차피 a-b에서 a를 포함하여 인덱싱 되어있다.)

Standard