MongoDB 기초 공부

인덱스

  • 단일 값이나 단일 필드는 인덱스를 생성해도 필드의 이름이 인덱스에 저장되지 않고 인덱스의 메타 정보에서만 관리되므로, 필드 이름의 크기는 인덱스의 크기에 영향을 미치지 않는다.
    하지만 서브 도큐먼트를 값으로 가지는 필드는 그 서브 도큐먼트의 자식 필드 이름이 모두 인덱스에 저장되므로 자식 필드의 이름이 길면 그만큼 인덱스의 크기에 영향을 미친다.
    서브 도큐먼트의 필드명이 길 때에는 이 값들이 인덱스의 크기를 훨씬 더 크게 만드는 역효과를 만들기도 한다.
  • WiredTiger 스토리지 엔진은 데이터 파일은 기본적으로 압축을 적용하지만, 인덱스 데이터 파일은 압축을 하지 않는다.
  • 인덱스 키값의 크기는 1KB를 넘을 수 없기 때문에 인덱스로 지정한 필드의 사이즈가 1KB 초과할 경우 오류를 발생시킨다.
  • 서브 도큐먼트 필드에 대해서 생성되는 인덱스를 사용하려면 조건절에 서브 도큐먼트의 필드를 모두 가지고, 순서가 같을 때만 인덱스를 활용할 수 있다.
  • 인덱스 인터섹션: MongoDB에서는 대부분의 DBMS와 다르게 하나의 컬렉션이 가진 두 개 이상의 인덱스를 검색하여 각 검색 결과를 만들고, 그 결과 집합의 교집합을 찾는 최적화를 제공한다.
    책에서는 인덱스 인터섹션이 효율적인 경우가 별로 없다고 한다.

*공간 인덱스와 전문 인덱스는 아직 나의 관심사가 아니므로 공부 대상에서 제외한다**

컴파운드 인덱스

  • 인덱스의 N번째 컬럼은 N-1번째 컬럼이 같은 레코드 내에서 다시 정렬된다. 그렇기 때문에 인데스 내에서 각 필드의 순서는 상당히 중요하며, 신중하게 결정해야 한다.
  • N번째 컬럼없이 N-1번째 컬럼만으로는 인덱스를 사용할 수 없다.
  • 해시 인덱스는 컴파운드 인덱스에서 사용할 수 없다. (B-Tree 알고리즘만 지원)

아래와 같은 도큐먼트가 있을 때

{
    "field1" : {
       "field1_1" : 111,
       "field1_2" : "aaa",
    },
    "field2" : "2020-08-16",
    "field3" : "박제희"
}
  • 인덱스가 하나의 필드를 가지면 단일 필드라고 정의한다. 여기서 단일 필드는 서브 도큐먼트도 포함한다.
    복합 필드 인덱스는 서브 도큐먼트의 필드도 포함할 수 있으며, 1레벨과 서브 도큐먼트의 필드와 혼용하는 것도 가능하다.
    • field1 를 지정하면 단일 필드이다.
    • field2 를 지정하면 단일 필드이다.
  • 인덱스가 복수의 필드를 가지면 복합 필드라고 정의한다. (컴파운드 인덱스 혹은 컴포지트 인덱스)
    • field1field2 를 지정하면 복합 필드이다.
    • field1.field1_1field1.field1_2 를 지정하면 복합 필드이다.
    • field1.field1_1field3 를 지정하면 복합 필드이다.

서브 도큐먼트를 갖는 필드(field1) 를 대상으로 단일 인덱스 (idx1) 를 생성했을 때와 복합 인덱스(idx2)를 생성했을 때는 어떤 차이점이 존재할까?
Ex)

  • idx1: field1 필드를 갖는 단일 인덱스
  • idx2: field1.field1_1field1.field1_2 필드를 갖는 복합 인덱스

idx1field1 에 어떤 서브 도큐먼트가 저장되든 BSON 으로 변환하여 인덱스 키 엔트리로 사용한다.
idx2field1 에 저장되는 서브 도큐먼트가 어떤 필드를 가지던지 관계없이 인덱스에 명시한 서브 필드의 조합으로 컴파운드 인덱스를 생성한다.

B-Tree 인덱스의 정렬 및 스캔 방향

  • 인덱스 내에서 각 필드를 어떻게 정렬할 것인지는 인덱스를 최초에 생성하는 시점에 결정된다.
  • 인덱스를 어떤 방향으로 읽을지는 쿼리가 원하는 값에 따라 옵티마이져가 만드는 실행계획에 따라 결정된다.

인덱스의 스캔 방향은 옵티마이저에 의해 결정되기 때문에, 인덱스 생성 시점에 조회 쿼리를 상상하며 ASC, DESC 를 고민할 필요는 없다.

컴파운드 인덱스에서 ASC 와 DESC 를 혼합해서 효과적으로 사용하는 경우는 어떤 경우인가?

  • 검색조건에 컴파운드 인덱스에 명시한 필드가 포함된다.
  • 정렬조건에 컴파운드 인덱스에 명시한 필드가 포함된다. (인덱스와 동일하게 혼합되어 있다면 인덱스 스캔 방향만 변경하여 탐색하면 되므로 인덱스에 최적화된 탐색이 가능하다)
  • 검색범위가 범위 검색이다 (동등 비교가 아니다)

B-Tree 인덱스

해시 인덱스

  • 필드값을 해싱하여 인덱스 키값을 관리하기 때문에 오직 동등성을 판단하는 검색에서만 인덱스를 사용할 수 있다.
  • 해시 인덱스는 컴파운드 인덱스로 생성할 수 없다. (서브 도큐먼트는 가능)
  • MongoDB의 해시 인덱스는 내부적으로는 B-Tree 알고리즘을 이용한다.
  • 인덱스를 생성할 때 인덱스 필드의 값을 MD5 알고리즘을 이용하여 저장하기 때문에 인덱스 필드의 크기와 관계없이 인덱스의 크기는 항상 8Byte이다.
  • B-Tree 인덱스와 장점보다 단점이 많은 알고리즘으로, 특수한 상황에 인덱스의 특징을 알아야 적절히 사용할 수 있음 (샤딩시 필수)

멀티 키 인덱스

하나의 도큐먼트에 하나의 인덱스에 대하여 여러개의 키가 존재하면 멀티 키 인덱스라고 부른다.
Ex) 아래와 같은 구조를 가지는 도큐먼트가 있을 때

{
    "id" : "1",
    "multi" : [
        1,
        2,
        3,
        4,
        5
    ]
}

multi 필드에 대하여 인덱스를 생성하면 하나의 인덱스에 5개의 인덱스 키가 생성되는데 이것을 멀티 키 인덱스라고 부른다.

멀티 키를 이용하여 범위 검색을 할 때, 의식의 흐름대로 BETWEEN 연산을 이용하듯이 find({ multi : { $gte : 3, $lte : 4 } }) 이런 쿼리를 작성하면 각 조건을 따로 비교한 다음에, 두 개의 결과를 병합한 결과를 리턴한다 (or 연산처럼 동작한다)

멀티 키를 이용하여 범위 검색을 하려면 $elemMatch 연산자를 이용해야 한다.
find({ multi : { $elemMatch: { $gte : 3, $lte : 4 } } })

제한사항

  • 샤드 키로 사용 불가
  • 해시 알고리즘 사용 불가
  • 커버링 인덱스 처리 불가

프라이머리 키와 세컨드리 인덱스

  • MongoDB의 프라이머리 키는 강제되며, 무조건 _id 라는 이름으로 도큐먼트에 저장 되어야 한다.
  • 컬렉션마다 단 하나의 프라이머리 키만 가질 수 있으며, 나머지 인덱스는 모두 세컨드리 인덱스라고 한다.
  • 컬렉션이 샤딩 되어있는 경우에는 하나의 샤드에서 중복 값에 대한 체크는 MongoDB 서버에서 처리하지만, 각 샤드간 프라이머리 키 값의 중복 체크는 응용 프로그램에서 처리해야 한다.

유니크 인덱스

  • NULL 값을 제한하지 않는다
  • NULL 과 존재하지 않는 값(도큐먼트에 필드를 명시하지 않음)을 동일하게 취급한다
  • 도큐먼트 간의 중복값은 체크를 하지만, 도큐먼트 내부에서 중복된 값을 체크하지는 않는다.
  • 샤딩이 적용된 경우에는 샤드키를 선행 필드로 가지는 인덱스에서만 유니크 인덱스를 생성할 수 있다.
  • 해시 알고리즘을 적용할 수 없다. (해시 충돌 가능성을 가지고 있기 때문)
  • Mongo DB 의 유니크 인덱스는 일반 인덱스와 동일한 역할을 하므로 중복해서 인덱스를 생성하지 않도록 한다.

Sparse 인덱스 와 Partial 인덱스

  • Sparse 인덱스
    • 인덱스 대상 필드를 명시한 도큐먼트만 인덱스에 키 엔트리를 저장한다. (NULL 포함)
    • 컴파운드 인덱스인 경우에는 인덱스를 구성하는 필드 중에서 하나라도 명시하면 인덱싱 대상이 된다.
    • 인덱스가 커버할 수 없는 쿼리에서는 인덱스를 사용할 수 없다
  • Partial 인덱스 : 특정 조건에 따라 인덱싱 가능
    • 인덱스를 생성할 때 partialFilterExpression 옵션을 이용하여 인덱스 생성 조건을 명시
    • 인덱스 생성 조건에는 인덱스 대상 필드와 무관한 필드에 대해서도 조건으로 사용하는 것이 가능하다.
    • Partial 인덱스를 사용하려면 인덱스 생성 조건과 일치하거나 부분 범위의 조건을 명시해야만 한다
    • 샤드키에 사용 불가
    • 인덱스가 커버할 수 없는 쿼리에서는 인덱스를 사용할 수 없다

불완전한 결과

Sparse 인덱스나 Partial 인덱스는 컬렉션에 저장되는 도큐먼트가 특정 조건을 만족하는 경우에만 인덱싱을 진행하게 된다.
그렇기 때문에 인덱스로 참조할 수 없는 도큐먼트가 분명히 존재하게 되어 인덱스 탐색을 하게 될 경우에 불완전환 결과를 리턴할 수 있다.
그래서 옵티마이저는 인덱스가 커버할 수 없는 쿼리라고 판단하면 인덱스를 사용하지 않도록 하고 있지만, 사용자가 인덱스 힌트를 직접 명시하면 옵티마이저는 사용자가 이런 불완전한 결과에 대한 가능성을 충분히 인지하고 있다고 가정하고 인덱스 탐색을 하도록 실행 계획을 수립하도록 한다.

예시)

// 컬렉션과 Sparse 인덱스 생성
db.createCollection("sample");
db.sample.createIndex({
    "field" : 1
}, {
    "sparse" : true
});

// 더미 데이터 생성
db.sample.insert({
    _id : "1",
    "name" : "AAA",
    "field" : 1
});
db.sample.insert({
    _id : "2",
    "name" : "BBB",
    "field" : 2
});
db.sample.insert({
    _id : "3",
    "name" : "CCC",
    "field" : null
});
db.sample.insert({
    _id : "4",
    "name" : "DDD"
});

// 인덱스를 사용하지 않지만 완전한 결과
// 3과 4 도큐먼트를 리턴한다.
db.sample.find({
   "field" : null
});

// 인덱스를 사용하지만 불완전한 결과
// 3 도큐먼트만 리턴한다.
db.sample.find({
   "field" : null
}).hint({ "field" : 1 });

TTL 인덱스

컬렉션의 도큐먼트가 언제까지 유효한지를 판단하여 더 이상 유효하지 않은 도큐먼트는 자동으로 삭제되게 하는 기능의 인덱스
도큐먼트 삭제는 TTL Monitor 라는 쓰레드가 지정된 시간 간격(기본:1분)마다 지정된 시간보다 오래된 도큐먼트를 찾아서 삭제한다.
TTL 인덱스는 쿼리의 검색 성능 향상 목적보다는 TTL Monitor 쓰레드가 삭제할 도큐먼트를 찾기 위한 인덱스로 보는게 정확하다.

TTL 인덱스는 지정된 필드가 Date 타입이거나 Date 타입의 값을 배열로 가지는 필드에 대해서만 자동 삭제가 실행된다.

예시 )

db.log.createIndex( { "created" : 1 }, { "expireAfterSeconds" : 600 } )

// TTL Monitor 수도코드
if (created + 600 < NOW) remove();
db.token.createIndex( { "expiredDateTime" : 1 }, { "expireAfterSeconds" : 0 } )

// TTL Monitor 수도코드
if (expiredDateTime < NOW) remove();

Partial 인덱스 옵션과 조합하여 선별적 자동 삭제도 가능하다.

예시 )

db.log.createIndex( { "created" : 1 }, {
    "created" : 600,
    "partialFilterExpression" : { "is_admin" : false }
})

// TTL Monitor 수도코드
if (created + 600 < NOW && isNotAdmin()) remove();

인덱스 콜레이션

MongoDB는 모든 문자열 비교에서 대소문자를 구분하는 Case-Sensitive 방식이다.

db.data.insert({ name : "jehui" });

db.data.find({ name : "Jehui" });
// >> not found

3.4 버전 부터 컬렉션과 인덱스에 대해서 콜레이션을 지정가능 컬레이션을 지정하면 영어의 대소문자 비교 구분뿐만 아니라 국가별 언어에 맞는 정렬 및 비교 규칙을 사용자의 요구사항에 맞게 적절히 선택할 수 있다.

  • 컬렉션의 인덱스를 생성한 이후에는 다른 콜레이션으로 변경할 수 없다. (콜레이션을 변경하라면 인덱스 삭제 -> 생성 과정을 거쳐야 한다.)
  • 인덱스를 생성할 때 별도의 콜레이션을 명시하지 않으면 컬렉션의 기본 콜레이션을 상속받게 된다.

잠금

WiredTiger 스토리지 엔진은 글로벌(인스턴스 레벨) 잠금과 데이터베이스와 컬렉션 레벨의 인텐션 잠금을 지원한다.
인텐션 잠금은 데이터베이스 레벨이나 컬렉션 레벨의 명령과 도큐먼트 레벨의 명령이 최적의 동시성을 유지하면서 실행될 수 있게 해준다.

잠금 양보 (Yield)

MongoDB 서버는 트랜잭션보다는 높은 동시성 처리의 우선순위가 더 높기 때문에 설정된 시간보다 오래 걸리거나, 많은 자원을 소모하는 경우에는 잠깐 쉬었다(Yield)가 다시 처리한다.

  1. 읽기를 위한 IS 잠금(Intention Shared) 획득 (REV1 스냅샷 획득)
  2. 데이터 탐색
  3. 잠금 양보 (스냅샷 해제)
  4. 읽기를 위한 IS 잠금(Intention Shared) 획득 (REV2 스냅샷 획득)
  5. loop …

이러한 특성 때문에 쿼리 일관성이 깨질 수 있다는 문제점을 가지고 있음

관련 설정

  • internalQueryExecYieldIterations
  • internalQueryExecYieldPeriodMs

트랜잭션

2.6 버전 까지 사용되던 MMAPv1 스토리지 엔진은 트랜잭션이라고 할 만한 요소가 별로 없다. (대부분 운영체제 시스템 콜에 의존)

WiredTiger 스토리지 엔진이 제공하는 트랜잭션의 ACID 속성

  • 최고 레벨의 격리 수준은 SNAPSHOT(RR)
  • 트랜잭션의 커밋과 체크포인트 2가지 형태로 영속성 보장
  • 커밋되지 않은 변경 데이터는 공유 캐시 크기보다 작아야 함

MongoDB 서버에 내장된 WiredTiger 의 격리 수준은 SNAPSHOT으로 고정되어 있다.
MongoDB 서버는 WiredTiger 스토리지 엔진의 SNAPSHOT 격리수준을 100% 지원하지는 않는다.

쓰기충돌 (Write Conflict)

MongoDB는 쓰기 충돌이 발생하면 RDBMS 와는 다른 방식으로 해결한다.
image

변경하고자 하는 도큐먼트에 잠금이 걸려있을 경우에는 즉시 업데이트 실행을 취소하고 재시도를 수행한다.
이런 과정들은 MongoDB 서버 프로세스 내부에서만 실행되기 때문에 응용 프로그램에는 투명하게 작동한다.

기존 RDBMS 는 쓰기 충돌이 발생하면 잠금 대기상태에 들어가기 때문에 커넥션이 일시적으로 증가하는 반면에 MongoDB 는 UPDATE 문을 재시도하는 방식이기 때문에 하나의 도큐먼트에 변경이 집중되면 쓰기 충돌→재시도 과정이 반복되어 CPU 의 사용량이 높아지는 현상이 발생될 수 있다. (CPU 사용량만 증가하고 처리능력은 저하된다)

단일 도큐먼트 트랜잭션

MongoDB 서버는 단일 도큐먼트의 트랜잭션만 지원한다. 단일 도큐먼트의 변경에 대해서는 원자 단위의 처리가 보장 되지만, 문장 단위의 트랜잭션은 지원되지 않는다. (MongoDB 서버에서 작업을 쪼개서 진행)

예시)

db.user.insert([
  {_id:1, name:"AAA"},
  {_id:1, name:"BBB"},
])

// 몽고DB 수도코드
BEGIN
    db.user.insert({_id:1, name:"AAA"})
END

BEGIN
    db.user.insert({_id:1, name:"BBB"})
END
db.user.find()
// > {_id:1, name:"AAA"}

{multi:true} 옵션을 명시한 UPDATE 명령도 동일하다

MongoDB 서버의 격리 수준과 정렬

MongoDB 서버는 도큐먼트를 조회할 때 기본적으로 스냅샷을 기반으로 처리하며 스냅샷은 도큐먼트 건수나 일정 시간을 기준으로 초기화가 진행된다.
그런데 실행하는 쿼리가 정렬이 필요하다면 MongoDB 서버는 컬렉션의 데이터를 모두 가져와서 메모리에 적재하고 정렬을 실행하여 그 결과를 클라이언트로 보내게 된다.

요약하면 정렬이 포함되지 않거나 정렬이 인덱스로 보장될(정렬 SKIP) 때에는 스냅샷 격리 수준이 제한적으로만 보장된다.

ReadConcern, WriteConcern, ReadPreference

ReadConcernWriteConcern 는 데이터 읽기/쓰기를 시도할 때 동기화 수준을 결정 ReadPreference 는 레플리카 셋의 어느 MongoDB 서버로 요청을 보낼 것인지 결정

WriteConcern

클라이언트의 변경요청을 어디까지 처리하고 응답을 내려보낼 것인지 결정하는 옵션으로 클라이언트, 데이터베이스, 컬렉션 수준까지 지정이 가능하다

  • UNACKNOWLEDGED 변경 요청에 대한 응답을 기다리지 않는다
  • ACKNOWLEDGED 변경 내용을 메모리까지만 적용하고 클라이언트로 응답을 보낸다 (기본값)
  • JOURNALED 변경 내용을 저널로그까지 기록하고 클라이언트로 응답을 보낸다 (2.6 버전부터 지원)
  • FSYNC 변경 내용을 데이터파일까지 기록하고 클라이언트로 응답을 보낸다.
    • MMAPv1 스토리지 엔진에서 사용되던 옵션
    • WiredTiger 스토리지 엔진은 이 스펙을 지원하지 않는다 (저널로그로 대체)
    • 클라이언트의 드라이버에서 제거될 옵션이다

image

레플리카 셋 간의 동기화 제어

레플리카 셋에서 변경 결과를 어느정도 수준으로 동기화 시킬건지 제어할 수 있다.

  • { w : 2 } 레플리카 셋에서 2대 이상의 멤버가 WriteConcern 조건을 만족 해야한다.
  • { w : "majority"} 레플리카 셋에서 과반수 이상의 멤버가 WriteConcern 조건을 만족 해야한다.

ReadConcern

WriteConcern 옵션에 따라 MongoDB 레플리카 셋의 멤버들은 서로 다른 상태를 가질 수 있다.
이런 특징이 데이터를 읽어갈 때 문제점을 유발할 가능성이 있기 때문에 데이터 읽기를 일관성 있게 유지할 수 있도록 지원하는 옵션이다

  • local 쿼리가 실행되는 MongoDB 서버가 가진 최신의 데이터를 반환 (기본값)
  • majority 레플리카 셋에서 과반수 이상의 멤버가 가진 최신의 데이터를 반환
    • WiredTiger 스토리지 엔진만 지원
    • 3.2 이상부터 지원
    • enableMajorityReadConcern 옵션 활성화가 전제조건
  • linearizable 레플리카 셋의 모든 멤버가 가진 변경 사항에 대해서만 쿼리 결과를 반환
    • 3.4 이상부터 지원
    • 프라이머리 멤버에서 쿼리를 실행할 때만 사용가능
    • 레플리카 셋의 특정 멤버에 장애가 발생할 경우 무한정 대기상태에 빠질 수 있기 때문에 쿼리 타임아웃을 지정하는 것을 권장

ReadPreference

드라이버 수준에서 클라이언트의 쿼리를 어떤 MongoDB 서버로 요청해서 실행할 것인지 결정하는 옵션이다.

  • primary 프라이머리 멤버로만 쿼리를 요청한다 (기본값, 레플리카 셋에서 프라이머리 멤버가 없으면 쿼리 실행은 실패)
  • primaryPreferred 레플리카 셋에서 프라이머리 멤버가 없으면 세컨드리 멤버로 쿼리를 요청한다
  • secondary 쿼리를 레플리카 셋의 세컨드리 멤버로만 요청한다 (세컨드리 멤버가 없으면 쿼리 실행은 실패)
  • secondaryPreferred 레플리카 셋에서 세컨드리 멤버가 없으면 프라이머리 멤버로 쿼리를 요청한다
  • nearest 레플리카 셋에서 쿼리의 응답 시간이 빠른 멤버로 쿼리를 요청한다. (레플리카 셋의 멤버들이 글로벌하게 분산되어 있을 경우에 적절한 옵션)

primaryPreferredsecondaryPreferred 옵션을 사용하는 경우에 일시적인 장애로 커넥션에서 쿼리를 처리하는 멤버가 변경 되었을 경우에는 커넥션이 종료될 때까지 쿼리를 처리하는 멤버는 변경되지 않는다

세컨드리 읽기는 복제 지연에 대해 검토한 후에 적용하는 것이 좋다.

  • 세컨드리 멤버는 OpLog 를 적용할 때 글로벌 잠금을 획득한다
  • 세컨드리에서 실행되는 무거운 쿼리는 OpLog 적용을 지연시킨다
  • MongoDB 매뉴얼에서는 세컨드리 읽기를 가능하면 사용하지 않는 것을 권장한다 (정확히는 사용자 서비스를 세컨드리 읽기에서 처리하는 것)

데이터 모델링

컬렉션

MongoDB 의 매뉴얼에서 MongoDB는 조인을 지원하지 (3.2부터 제한적으로 지원) 않기 때문에 MongoDB의 도큐먼트는 가능하면 많은 데이터를 포함할 것을 권장하고 있다.

이 권장사항은 조인 제거, 트랜잭션 보장등의 장점도 있는 반면에 단점도 존재하기 때문에 데이터 특성에 따라 적절한 트레이드 오프가 필요하다

도큐먼트의 크기가 커지면 발생하는 단점

  • 메모리 캐시 효율 저하
  • 디스크 읽기 오퍼레이션 증가
  • 네트워크 사용량 증가
  • 도큐먼트의 최대 사이즈는 16MB

  • 성능상의 장점은 없다
  • Materialized View, Updatable View 와 같은 기능은 제공하지 않는다
  • 컬렉션을 캡슐화함으로써 얻는 장점을 제공
  • 생성된 뷰는 컬렉션처럼 취급하면 된다 db..find(), db..drop() 등등

BSON 도큐먼트

MongoDB는 도큐먼트를 BSON으로 처리한다 BSON 스펙을 한번 훑어본다면 MongoDB 사용에 도움이 될 것이다.

BSON이 갖고 있는 특징

  • Lightweight 공간 최적화 : 단순히 문자열로 저장하지 않고 데이터 타입에 맞추어 이진 데이터 타입으로 저장한다
  • Traversable 빠른 탐색 : BSON 도큐먼트는 도큐먼트의 크기, 필드 값의 데이터 타입, 필드 값의 크기와 같은 정보를 포함하고 있어서, 빠른 탐색이 가능 하도록 설계되어 있다
  • Efficient 효율적 : C 언어의 Primitive 타입을 이용하기 때문에 대부분의 언어에서 빠르게 처리가 가능하다

도큐먼트 구성
4바이트 (도큐먼트 크기)
1바이트 (필드 값의 타입)
필드 명
0x00 (필드 명의 끝)
4바이트 (필드 값의 크기)
필드 값
0x00 (필드 값의 끝)
0x00 (도큐먼트의 끝)

BSON 이해를 위한 샘플 코드

/**
 * expected.
 *
 * {"key":"value"}
 */
final var expected = "{\"key\": \"value\"}";
byte [] INPUT_BSON = new byte[]{
    0x14, 0x00, 0x00, 0x00, // 도큐먼트 사이즈 20
    0x02, // 문자열 필드
    0x6b, 0x65, 0x79, // 필드명: key
    0x00, // 필드명의 끝 (null)
    0x06, 0x00, 0x00, 0x00, // 필드 길이: 6 (null 포함)
    0x76, 0x61, 0x6c, 0x75, 0x65, // 필드: value
    0x00, // 필드의 끝 (null)
    0x00 // 도큐먼트의 끝 (null)
};

정규화, 역정규화

아래는 참고서적에 적힌 내용중 하나 KakaoTalk_20200908_234751372

쿼리 개발 및 튜닝

  • 맵 리듀스
  • Aggregation

쿼리 실행계획

  • 쿼리 실행계획 분석

참고서적

Back to Top ↑

댓글남기기