■ 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
■ Javascript

[Angular.js] 앵귤러JS – ng-repeat에 있는 track by에 대한 설명

angular.js에서 ng-repeat를 사용할때track by란 것이 있습니다.

이것에 대해서 알아보도록하겠습니다.

ng-repeat는 directive입니다. 우리나라 말로 지시자라고 하는데, 앵귤러에서는 해당 지시자를 활용해서

여러가지 templete적인 부분과 plugin 형태의 개발을 함축해 놓았습니다.

 

ng-repeat를 이용해서 기존 템플릿의 for문을 구현할 수 있습니다.

<any ng-repeat="value in array"> </any>

를 사용하면 <any></any> 사이에 해당 scope가 자동으로 생기게 됩니다.

그러면서 $index, $first, $middle … 등과 같은 이미 정의된(pre-defined) 요소들의 값이 할당됩니다.

 

그런데 잘나가다가 하나 이상한 것이 들어오게 됩니다. 그것은 바로 track by란 놈인데,

표현식은 다음과 같습니다.

<any ng-repeat="value in array track by exp"> </any>

exp 부분에 해당하는 속성을 넣어주어야 합니다.

해당하는 속성이란 부분이 매우 애매한데 좀더 풀어서 설명하도록 하겠습니다.

자바스크립트에서 배열은 타 언어와 다르게 중복값을 허용합니다.

즉 array부분에 중복값이 있다면 반드시 distinct할 exp를 작성해 주어야 합니다.

쉽게 예제를 보면

 

<any ng-repeat="v in [0,0,1]"></any>

란 코드가 있을때 0,0 두개의 중복값이 있습니다. track by를 작성해 주지 않으면 각각은 모두 별개의 값으로서 인식되나 키값은 중복값을 허용하지 않게됩니다. ($$hashKey 이용)

여기선 $$hashKey에 키값으로 0이 2개가 들어가서 결국엔 [0, 1]과 같은 해쉬가 생성됩니다.  따라서 for문은 3바퀴를 도는데 값은 두개밖에 없습니다.

즉 에러가 나게 됩니다.

따라서

<any ng-repeat="v in [0,0,1] track by $index"></any>

라고 작성하면 $hashKey가 아닌 $index(위치)를 통해서 다른 3개의 DOM을 생성합니다.

 

그럼 간단하게 track by에 대해서 설명을 마치겠습니다. 안녕~

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