이전까지 shard key는 한 번 정하면 수정할 수 없었으나,

4.4 버전부터 refineCollectionShardKey를 통해 suffix를 붙이는 형태로 부분 수정이 가능해졌고,

5.0 버전부터 shard key를 완전히 변경하는 reshard가 가능해짐.

 

 

refineCollectionShardKey

v 4.4 부터 가능.

지정한 shard key의 cardinality가 부족해서 chunk가 제대로 나뉘지 않아 jumbo chunk가 생성되거나 할 때,

shard key의 뒷부분에 cardinality를 높여주는 필드를 하나 더 추가하는 방법.

// 만약 shard key가 이렇고
user_id: 1

// 여기에 order_id를 추가해서 shard key를 만들고 싶다면, 우선 해당 인덱스를 하나 만들고
db.COL_NAME.createIndex({user_id:1, order_id:1})

// refineCollectionShardKey 실행
db.adminCommand({
    refineCollectinShardKey: 'DB_NAME.COL_NAME',
    key: { user_id:1, order_id:1 }
})

// sh.status()로 확인해보면 shardKey 변경 확인가능

unique index인 경우라든지 신경써야 되는 부분들이 있으므로 공식문서 참조할 것.

https://www.mongodb.com/docs/manual/reference/command/refineCollectionShardKey/

 

 

 

 

Reshard

v 5.0 부터 가능

 

필수 확인 사항

다음의 조건을 만족하지 않는다면 reshard가 힘들 수 있음.

  • reshard 되는 동안 2초 간의 write block을 견딜 수 있어야 함
    • 어지간하면 이정도는 괜찮지 않을까...
  • 아래 하드웨어 조건을 만족해야 함
    • 최소 컬렉션 사이즈의 1.2배의 여유공간
      • ex) 컬렉션 사이즈가 1T 라면 1.2T의 여유공간 필요
    • I/O가 50% 이하
    • CPU load가 80% 이하
  • 다음 둘 중 하나를 해줘야 함
    • 어플리케이션의 query가 이전 샤드키와 새로 변경할 샤드키를 모두 포함하거나
    • 어플리케이션을 중단하고
      • 어플리케이션의 query가 새로운 샤드키를 포함하게 하기
      • $currentOp를 통해 reshard가 완료될 때까지 대기한 다음
      • 어플리케이션 재배포
    • 만약 이전 샤드키와 새로 변경할 샤드키를 모두 포함하지 않거나 _id 를 포함하지 않는다면 다음 query들은 에러를 발생시킴
      • deleteOne(), findAndModify(), findOneAndDelete(), findOneAndReplace(), findOneAndUpdate(), replaceOne()
    • 최적의 성능을 위해 다른 query들도 새로운 샤드키를 포함하도록 변경하는 것을 권장
  • 실행중인 인덱스 빌드가 없어야 함
    • db.currentOp() 로 확인 가능

 

제한사항

  • 한 번에 하나의 컬렉션만 reshard 가능
  • writeConcernMajorityJournalDefault는 반드시 true
  • unique index가 있는 컬렉션은 reshard 불가능
  • unique index는 새로운 샤드키가 될 수 없음
  • reshard 중 다음 명령어들은 사용할 수 없음
    • collMod, convertToCapped, createIndexes, createIndex(), drop, drop(), dropIndexes, dropIndex(), renameCollection, renameCollection()
    • addShard, removeShard, db.createCollection(), dropDatabase
  • shard된 time series 컬렉션은 reshard 불가

 

reshard 과정

mongos에 접속해서 reshardCollection 실행

db.adminCommand({
    reshardCollection: 'DB_NAME.COL_NAME',
    key: SHARD_KEY
})

 

reshard 과정 모니터링

db.getSiblingDB('admin').aggregate([
    { $currentOp: { allUsers: true, localOps: false } },
    {
        $match: {
            type: 'op',
            'originatingCommand.reshardCollection': 'DB_NAME.COL_NAME'
        }
    }
])
 
// 그럼 아래와 같은 결과가 나옴
[
    {
        shard: SHARD_NAME,
        type: 'op',
        desc: 'ReshardingRecipientService 혹은 ReshardingDonorService 혹은 ReshardingCoordinatorService RESHARDING UUID',
        op: 'command',
        ns: 'DB_NAME.COL_NAME',
        originatingCommand: {
            reshardCollection: 'DB_NAME.COL_NAME',
            key: SHARD_KEY,
            unique: <boolean>,
            collation: { locale: 'simple' }
        },
        // 경과시간
        totalOperationTimeElapsedSecs: <number>,
        // 예상 남은시간
        remainingOperationTimeEstimatedSec: <number>,
        ...
    },
    ...
]

 

reshard 완료

  • reshard가 진행되다가 예상 남은시간이 2초 이하가 되면 MongoDB는 write를 block하고 reshard를 마무리 함
  • reshard가 완료되면 ok: 1 반환
{
    ok: 1,
    '$clusterTime': {
        clusterTime: <timestamp>,
        signature: {
            hash: Binary(Buffer.from("0000000000000000000000000000000000000000", "hex"), 0),
            keyId: <number>
        }
    },
    operationTime: <timestamp>
}

 

sh.status()로 reshard가 완료되었는지 확인.

 

 

reshard 중지는 아래 방법으로 가능.

// 다음 명령어로 실행 중인 reshard 중지 가능
db.adminCommand({
    abortReshardCollection: 'DB_NAME.COL_NAME'
})

 

 

기타 신경써야 하는 부분들 있으므로 공식문서 참조할 것.

https://www.mongodb.com/docs/manual/core/sharding-reshard-a-collection

 

 

Posted by bloodguy
,