MongoDB의 점보청크에 대해서 알아보자.
청크란? Chunk 란?
MongoDB Sharded Cluster의 경우 데이터를 논리적인 단위로 구분하여 샤드에 저장한다. 이 단위를 청크라고 한다. MongoDB에서는 청크를 각 샤드에 나눠 저장하여 데이터를 분산하여 저장한다. 이 청크는 기본적으로 사이즈 혹은 범위가 정해져 있는데, 특정 청크가 과도하게 커지는 것을 방지하고 이를 통해서 결론적으로는 특정 샤드의 데이터와 부하 쏠림현상을 방지한다.
MongoDB 6.0 버전 전까지 기본 청크 사이즈는 64MB, 6.0 버전부터는 기본 청크 사이즈가 128MB로 변경되었다. 6.0 버전 전까지는 청크의 갯수를 기준으로 샤드간의 데이터가 잘 분배되었는지 확인했다. 가장 많은 청크 갯수를 가진 샤드가, 가장 적은 청크 갯수를 가진 샤드보다 N개 이상 많으면 청크를 이동시키는 청크 마이그레이션 작업을 진행한다. 이런 방식으로 데이터와 부하의 균형을 맞췄다. 6.0 버전 이상부터는 청크의 갯수가 아닌 샤드 내 데이터사이즈를 기준으로 균형을 맞춘다. 데이터가 가장 많은 샤드가 데이터가 가장 적은 샤드보다 3 * 기본 청크 사이즈 만큼 데이터가 많으면 청크 마이그레이션이 시작된다.
MongoDB 6.0 이전까지는 청크를 기본 청크 사이즈로 엄격하게 잘라서 유지했다면, 6.0 이후 부터는 청크를 더 이상 자르지 않고 오히려 Chunk Defragmentation 과 같이 청크를 합쳐서 보관하는 것으로 보인다. 왜 이런 방향성을 갖게 되었는지는 Chunk 의 갯수의 증가에 따라서 발생하는 문제를 보면 알 수 있다.
MongoDB는 청크에 대한 정보를 config server 에 저장한다. 청크의 갯수가 증가되면 config server에 저장되는 document의 갯수가 증가한다. 청크가 적을때에는 문제가 없지만, 청크의 갯수가 1,000,000 개 이상이 되는 경우 문제가 발생할 수 있다. Sharded Cluster의 작업 중에서 config server의 chunk에 관련된 document 들의 데이터들을 lock 을 잡는 작업들이 있다. 청크에 관련된 document 들이 1,000,000 개 이상이 되게 되면 lock 을 잡고 푸는데 걸리는 시간이 증가하게 되고 이 시점에 Sharded Cluster에 들어오는 쿼리들에 응답을 하지 못하는 문제들이 생겼던 것 같다. 정확하게는 어떤 작업이 문제가 되었는지 잘 모르겠지만, 청크의 갯수가 증가함에 따라서 Sharded Cluster의 성능에 문제가 생겼던 것은 맞는 것 같다. 이런 문제들로 인해서 6.0 버전 이상부터는 이런 청크의 갯수를 줄이기 위해 부단한 노력을 하고 있는 것으로 보인다.
점보청크란? Jumbo Chunk 란?
청크는 논리적인 데이터단위로 데이터의 범위를 가진다. Shard Key로 설정된 데이터의 범위를 가지게 된다. 이 데이터의 범위가 논리적으로 더 이상 분리될 수 없는 경우 + 이 청크의 크기가 특정 임계치를 넘는 경우 이 청크는 점보청크가 된다. 예를 들어서 a 필드를 기준으로 Hashed Sharding을 하게 된 경우, 동일한 a 필드의 값을 가진 데이터가 특정 범위를 넘어간 경우 이 데이터를 다른 청크로 쪼갤 수 없기 때문에 점보청크가 된다.
점보청크가 되면 sh.status() 시에 jumbo: true 플래그가 노출되거나, config 데이터베이스의 db.settings.chunk 컬렉션에서 도큐먼트의 jumbo: true 필드가 생기게 된다.
그럼 정확하게 특정 청크가 점보청크가 되는 데이터 사이즈는 몇 MB 일까?
JIRA : https://jira.mongodb.org/browse/SERVER-60267
특정 청크가 점보청크가 되는 데이터사이즈는 기본 청크 사이즈 * 2 이다. MongoDB 6.0 버전 이전에서는 기본 청크 사이즈가 64MB 이므로 128MB 가 넘으면 점보청크가 되고, MongoDB 6.0 버전 이상에서는 기본 청크사이즈가 128MB 이므로 청크가 256MB 가 넘으면 점보청크가 된다. 이 사이즈의 기준은 도큐먼트의 논리적인 사이즈를 기준으로 하며, MongoDB에서 압축하여 데이터가 디스크에 저장되었을 때의 크기인 물리적 사이즈가 아니다.
MongoDB에서는 청크의 데이터 사이즈가 변경될 때 마다 점보청크인지 확인할까?
DML 이 들어올 때 마다 점보청크 여부를 확인한다면 불필요한 계산이 너무 많아진다. MongoDB 에서는 청크가 moveChunk 되는 시점에 청크가 점보청크인지 확인한다.
int Balancer::_moveChunks(OperationContext* opCtx,
const MigrateInfoVector& chunksToRebalance,
const MigrateInfoVector& chunksToDefragment) {
.
.
.
if (status == ErrorCodes::ChunkTooBig || status == ErrorCodes::ExceededMemoryLimit) {
++numChunksProcessed;
LOGV2(21871,
"Migration {migrateInfo} failed with {error}, going to try splitting the chunk",
"Migration failed, going to try splitting the chunk",
"migrateInfo"_attr = redact(migrateInfo.toString()),
"error"_attr = redact(status));
const CollectionType collection = catalogClient->getCollection(
opCtx, migrateInfo.uuid, repl::ReadConcernLevel::kMajorityReadConcern);
ShardingCatalogManager::get(opCtx)->splitOrMarkJumbo( <-- moveChunk 과정에서 chunk가 너무 큰 경우, chunk를 split 하거나 Jumbo chunk로 만듭니다.
opCtx, collection.getNss(), migrateInfo.minKey, migrateInfo.getMaxChunkSizeBytes());
continue;
}
.
.
.
그럼 점보청크를 확인할 때 청크 내 도큐먼트의 데이터 사이즈를 정확하게 확인하는가?
그렇지 않다. MongoDB 는 통계정보를 기반으로 점보청크를 검사한다. 컬렉션의 avg document size 를 기준으로 다음과 같은 공식을 사용한다.
Jumbo Chunk 여부 = chunk's document count > 2 * Configured Chunk Size / average document size
sh.moveChunk() - MongoDB Manual v8.0
This command cannot be used for hashed shard keys, as it uses the find option from the moveChunk command. To move a chunk with a hashed shard key, use the moveChunk command specifying the bounds field.
www.mongodb.com
많은 데이터베이스들에서 이런 통계정보를 기반으로 상태를 확인하고 동작한다. MongoDB도 마찬가지로 이런 통계정보를 저장하고 활용한다.
그러나 이런 통계정보를 사용하는 경우 문제가 되는 경우들이 종종 있다. 예를 들어서, 도큐먼트의 사이즈가 도큐먼트 별로 크게 달라질 수 있는 경우 점보청크의 실제 사이즈가 달라질 수 있을 것이다. 또한, 도큐먼트를 실제로 삭제하지 않고 delete: true 와 같이 플래그 형태로 관리하고 있는 경우도 문제가 될 수 있다.
하지만 장점으로는 점보청크가 되는 시점을 정확하게 계산할 수 도 있다. 실제 도큐먼트의 사이즈를 일일이 체크하기는 힘드니, 도큐먼트의 갯수를 모니터링하면 점보청크가 되는 시점이 예측 가능해지고 대응이 가능해진다.
점보청크는 정말 문제인가?
점보청크가 크게 문제가 되지는 않을 수 있다. Sharded Cluster의 모든 청크가 점보청크가 된다면, 그것은 샤드키 설계상의 문제다. 일부 청크가 점보청크가 된다면 샤드별로 몰리는 데이터 사이즈나 부하가 크지 않을 수 있다. 점보청크가 된다고 해서 곧바로 데이터 조회나 조작이 불가능해지는 것은 아니다. 그래서 크게 문제가 없을 수 도 있다.
하지만 점보청크가 되면 우선 청크의 움직임이 불가능해진다. 그리고 MongoDB의 소스코드 내에서 모든 상황에 대해서 점보청크에 대한 예외처리를 하지 않았을 가능성이 높다. 결국 점보청크가 있는 경우 Sharded Cluster의 동작이 불안정해질 가능성이 높다. 실제로 이 글을 작성하기 위해 테스트하는 경우에도, 점보청크를 만들었다가, 점보 플래그를 해결해주었음에도 불구하고 moveChunk 를 통해 청크 마이그레이션이 불가능한 경우가 있었다.
따라서 점보청크가 생기지 않도록 하는게 올바른 운영이 될 것이다. 점보 청크가 될 가능성이 있는 경우 설계를 변경하거나 refineShardKey 등을 통해서 문제가 발생하기 전 해결해야한다.