MongoDB Indexing & Shading

Indexing in MongoDB

  • DB 검색을 효율적으로 처리하기 위해 색인을 추가해서 데이터에 대한 빠른 탐색이 가능하도록 한다.
  • MongoDB에서는 B Tree 형태의 index을 구현한다.
  • 한 쿼리당 하나의 index만 유효
  • 생성/수정 과정에 있어 Index 수정 작업이 요구되기 때문에, 수정이 빈번하게 발생하는 경우에 대해서는 Index을 사용하는 것이 오히려 비효율적이다.
  • Memory 기반으로 데이터를 읽고/쓰기를 진행하기 때문에, Memory 크기가 충분하지 않는 경우, 디스크에서 데이터를 읽어오는 작업이 증가하게 되며 이에 따라 Thrashing이 발생하여 성능 저하를 초래하게 된다.

How to use Index in MongoDB

Test data 삽입

Enterprise test> db.person.insertMany([{"name":"A",age:12},{"name": "B", age:13},{"name":"C", age:23}])
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId("63a99c9a3789d610e1f76764"),
    '1': ObjectId("63a99c9a3789d610e1f76765"),
    '2': ObjectId("63a99c9a3789d610e1f76766")
  }
}

기존 index 조회

db.person.getIndexes()
[ { v: 2, key: { _id: 1 }, name: '_id_' } ]

default으로 기본키(_id)에 대한 index을 확인할 수 있다.

index 생성

ensureIndex({field},{option})

db.person.ensureIndex({"name" : 1}) //오름차순으로 정렬하고자 하면 1을, 내림차순이면 -1로 지정한다.
[ 'name_1' ]
Enterprise test> db.person.getIndexes()
[
  { v: 2, key: { _id: 1 }, name: '_id_' },
  { v: 2, key: { name: 1 }, name: 'name_1' }
]

db.person.ensureIndex({"name" : 1},{background: true}) //인덱스 생성작업을 background에서 동작하도록 한다. -> 보통 데이터의 수가 많은 경우 사용

db.person.ensureIndex({"name" : 1},{unique: true}) //고유 인덱스를 생성하고자 할 때 unique 속성을 지정한다. -> 중복된 데이터의 저장을 방지해서 데이터 검색 효율을 높인다. 

Enterprise test> db.person.ensureIndex({"name":1},{unique:true})
[ 'name_1' ]
Enterprise test> db.person.insertOne({"name":"A",age:12}) //unique index가 설정되어있는 field에 대해서 document을 추가하면 아래와 같이 에러가 발생한다.
MongoServerError: E11000 duplicate key error collection: test.person index: name_1 dup key: { name: "A" }

db.person.ensureIndex({"name" : 1},{unique: true, dropDups:true}) //dropDups을 true로 설정하면 기존의 데이터에서 중복을 제거한다.

index 삭제

dropIndex({field})

db.person.dropIndex({"name": 1})

Enterprise test> db.person.getIndexes()
[
  { v: 2, key: { _id: 1 }, name: '_id_' },
  { v: 2, key: { name: 1 }, name: 'name_1' }
]
Enterprise test> db.person.dropIndex({"name":1})
{ nIndexesWas: 2, ok: 1 }
Enterprise test> db.person.getIndexes()
[ { v: 2, key: { _id: 1 }, name: '_id_' } ]

dropIndexes을 이용하면 기본키 index(_id에 대한 index)을 제외한 모든 index를 제거한다.

db.person.dropIndexes();

Enterprise test> db.person.getIndexes()
[
  { v: 2, key: { _id: 1 }, name: '_id_' },
  { v: 2, key: { name: 1 }, name: 'name_1' }
]
Enterprise test> db.person.dropIndexes()
{
  nIndexesWas: 2,
  msg: 'non-_id indexes dropped for collection',
  ok: 1
}
Enterprise test> db.person.getIndexes()
[ { v: 2, key: { _id: 1 }, name: '_id_' } ]

Using Index

Test data 삽입

만 개의 document을 추가해보자

for(i=0;i<1000000;i++){ db.users.insertOne({"i":i,"username":"user"+i,"age":Math.floor(Math.random()*120),"created": new Date()})}

index 사용하지 않은 경우

Enterprise test> db.users.find({username:"user101"}).explain("executionStats")

  executionStats: {
    executionSuccess: true,
    nReturned: 2,
    executionTimeMillis: 177,
    totalKeysExamined: 0,
    totalDocsExamined: 580290,

index을 사용하지 않고 데이터를 찾고자 할때는 177 ms가 소요되었다.

index 사용한 경우

Enterprise test> db.users.ensureIndex({"username":1})
[ 'username_1' ]
Enterprise test> db.users.find({username:"user101"}).explain("executionStats")

 executionStats: {
    executionSuccess: true,
    nReturned: 2,
    executionTimeMillis: 3,
    totalKeysExamined: 2,
    totalDocsExamined: 2,

index을 사용했을 때는 3ms 정도가 소요됨을 확인할 수 있다. 뿐만아니라, 총 조회된 document 수를 보더라도 큰 차이를 보임을 확인할 수 있다.

Shading

  • 많은 양의 데이터를 하난의 서버에서 보관하는 것은 불가능하다. scale-up을 해서 서버의 저장 용량을 늘리는 방향을 생각해 볼 수 있지만, 이는 서버의 대수를 늘리는 scale-out에 비해 비용이 많이 발생하게 된다.

  • No-SQL에서는 이러한 데이터를 분산해서 저장하는 shading을 기본적으로 저장하여 빅데이터를 저장할 수 있다.

  • 데이터를 분산하여 저장하므로써 데이터 유실로 인한 문제를 완화시킬 수 있다.

  • 쿼리가 여러 서버로 분산되어 병렬적으로 수행하게 되므로써 빠른 처리 성능을 보장받을 수 있다. 트래픽을 분산시키므로써 하나의 서버가 받는 부하를 줄이므로써 전체적인 성능을 높이는 것이다.

Shard key

  • MongoDB 샤딩 시스템 구축에 있어 가장 중요한 부분으로, 여러 개의 shard 서버를 분할 할 때 기준이 되는 필드이다. patition, load balancing의 기준이 되므로 shard key을 적절히 지정해야 성능을 높일 수 있다.

  • MongoDB replication set에서는 chunk migration 작업이 발생하게 되는데, 이는 데이터의 이동을 의미하는데, 서버에 균등하게 데이터를 재종하는 과정을 뜻한다. 즉, 하나의 서버에 저장되는 데이터의 양에 따라 migration 빈도와 횟수가 결정되게 되는데, shard key를 어떻게 설정하냐에 따라 서버에 저장되는 방식이 달라지기 때문에 적절한 shard key을 지정하는 것이 매우 중요하다.

  • 보통 데이터의 분포가 넓으면 High Cardinality, 낮으면 Low Cardinality라고 하는데, cardinality에 따라서 shard key을 지정하게 된다.

Shard Limitation

  • 한 chunk에 저장 가능한 BSON 객체의 개수는 250,000개
  • 한 chunk 의 분할지점은 최대 8192
  • 최대 샤드 노드의 개수는 1000개이다.
  • 분산 시스템라는 성질을 가지는 NoSQL의 특성상 데이터의 유실의 가능성을 항상 가지고 있다.(Master 노드가 비정상적으로 동작하는 환경에서 mongos는 master의 정상 동작 여부를 확인할 수 없기때문에, 그런 상태에서의 데이터 처리를 진행할 때에는 데이터 유실이 발생할 수 있다.) –> 중요한 데이터는 RDBMS을 활용해야한다.

shard에서 발생하는 데이터 유실의 문제를 해결하기 위해 replication 개념을 적용하여 primary-secondary 간에 데이터를 서로 복제하여 master에 대한 데이터 백업을 진행하여 master node가 동작하지 않더라도 slave가 대신해서 데이터를 처리할 수 있도록 한다.

References

댓글남기기