database

MongoDB - Sharding

샤딩(Sharding)은 데이터를 분산하여 저장하는 개념입니다. 대용량 트래픽과 방대한 데이터 집합을 다루는 MongoDB 환경에서, 샤딩(Sharding)은 확장성(Scalability)을 확보하기 위한 핵심 전략이라 할 수 있습니다. 하지만 여러 샤드 노드와 Config 서버, 그리고 Mongos를 결합해 운영하는 구조이기에, 동시성 제어와 트랜잭션 처리에도 특별한 고려가 필요합니다. 이번 글에서는 Mongos와 Config 서버의 역할, 샤딩 키와 청크(Chunk) 분할이 동시성에 미치는 영향, 그리고 Sharded Cluster에서 트랜잭션을 운용할 때 주의해야 할 점을 중심으로 살펴보겠습니다.

MongoDB 샤딩 구성 요소와 동시성

MongoDB에서 샤딩(Sharding)은 대규모 데이터 세트를 여러 노드에 분산 저장하여 확장성과 고가용성을 확보하는 핵심 기법입니다. 이를 위해선 Mongos(Query Router), Config 서버(Config Server Replica Set), Shard 노드라는 세 가지 주요 구성 요소가 협력해야 합니다. 각각의 역할을 이해하면 왜 MongoDB가 다양한 트래픽 상황에서도 안정적인 데이터 접근을 제공할 수 있는지 자연스레 알 수 있습니다.


Mongos(Query Router)

먼저, 클라이언트 애플리케이션이 MongoDB에 쿼리를 보낼 때, Mongos가 어떤 샤드(Shard)가 해당 데이터를 보관 중인지 파악하고 적절히 요청을 라우팅합니다. 이때, Mongos 자체에는 실제 데이터가 저장되지 않고, 그 대신 Config 서버에서 받아온 메타데이터를 캐싱해 두어 각 샤드에 어떤 청크(Chunk)가 있는지 빠르게 판별합니다. 이처럼 Mongos가 일종의 게이트웨이 역할을 담당하기에, 여러 샤드가 분산되어 있어도 클라이언트 쿼리는 마치 단일 데이터베이스에 요청하는 것처럼 편리하게 보낼 수 있습니다.


Config 서버(Config Server Replica Set)

샤딩 구성의 설계도와 같은 메타데이터는 Config 서버가 관리합니다. 예컨대 Shard 정보, Chunk 범위, 그리고 Chunk 이동이나 새로운 Sh드 추가 작업에 대한 기록을 저장해두죠. Config 서버 자체도 최소 3노드 이상의 Replica Set 구성을 권장하여, 장애 상황에서도 메타데이터를 안정적으로 보존합니다. 만약 Chunk를 옮기거나 샤드를 늘려야 한다면, Config 서버가 해당 정보를 갱신하고 Mongos가 이를 반영해 라우팅 로직을 업데이트합니다.

Shard 노드(Data-bearing Nodes)

실제 사용자 데이터는 Shard 노드에 저장됩니다. 여러 샤드가 합쳐져 전체 데이터 세트를 분담하므로, 특정 샤드가 모든 부하를 떠안지 않도록 샤딩 키 선정과 Chunk 분할을 적절히 수행하는 것이 중요합니다. 또한 각 Shard도 내부적으로는 Replica Set 구조를 취할 수 있어, 장애 복구와 데이터 무결성을 유지합니다. 즉, 어느 한 샤드가 다운되어도 복제본이 대신 역할을 이어받을 수 있도록 설계된 것이죠.

샤딩(Sharding)과 동시성

샤딩 환경에서의 동시성 제어는 결국 “각 샤드가 독립적으로 처리하는 연산”과 “전체 클러스터 수준의 트랜잭션·락” 간의 균형을 맞추는 문제라고 볼 수 있습니다. 예컨대 Mongos가 여러 샤드에 분산 쿼리를 보낼 때, 특정 Shard에서만 잠금이 걸렸다면 해당 부분만 지연되고 나머지 샤드는 정상 처리될 수 있습니다. 하지만 멀티 샤드 트랜잭션 등으로 여러 노드가 동시에 락을 필요로 하면, 2PC(2-Phase Commit) 같은 추가 메커니즘이 작동하며 전반적인 동시성에 영향을 미치게 됩니다. 따라서 샤딩을 설계할 때는 데이터 분산뿐 아니라, 락과 트랜잭션의 범위가 어느 수준에서 발생할지를 고려하는 것이 필수적입니다.


샤딩 키와 청크(Chunk) 분할이 동시성에 미치는 영향

샤딩 키(Shard Key)

MongoDB에서 샤딩을 설정할 때 가장 먼저 결정해야 하는 것은 “어떤 필드를 기준으로 데이터를 분할할 것인가?”입니다. 이 기준이 되는 필드가 바로 샤딩 키(Shard Key)인데, 이는 클라이언트가 어떤 데이터를 요청했을 때 그 요청이 어느 샤드(Shard)에 분산될지 결정하는 핵심 열쇠입니다. 샤딩 키가 잘못 설정되면 특정 샤드에만 트래픽이 쏠리는 병목현상이 발생하고, 반대로 샤딩 키가 균등하게 분포된다면 여러 샤드에 트래픽이 고르게 나뉘어 높은 동시성을 확보할 수 있습니다.

Chunk 분할과 이동의 메커니즘

MongoDB는 샤딩 키의 범위를 기준으로 데이터를 여러 조각(Chunk)으로 나누어 보관합니다. 만약 하나의 Chunk가 너무 커지면 자동 또는 수동으로 Chunk Split을 실행해 더 작게 쪼개고, 특정 샤드가 과부하인 경우 Chunk Migration으로 다른 샤드에 일부 데이터를 옮길 수 있습니다. 이 과정에서 쓰기·읽기 락이 걸리거나 지연이 발생할 수 있으므로, Chunk 이동이 잦아지지 않도록 샤딩 키를 적절히 설계하는 것이 중요합니다. 특히 Chunk 이동 시에는 컬렉션 단위나 Config 서버 메타데이터에 쓰기 잠금이 발생할 수 있어, 전체 클러스터 동시성에도 영향을 줄 수 있습니다.

샤딩 키의 위험성

종종 createdAt처럼 시간순으로만 증가하는 필드를 샤딩 키로 삼기도 합니다. 이 경우 최근 데이터는 특정 Chunk 범위에 집중되고, 해당 Chunk가 속한 샤드로 트래픽이 쏠릴 가능성이 큽니다. 이러한 상황을 흔히 Hot Shard 문제라고 부르는데, 한두 개의 샤드가 다른 샤드 대비 지나치게 바쁜 상태가 되어 전체적인 서비스 응답 속도를 떨어뜨릴 수 있습니다. 따라서 단순히 “자주 쓰는 필드니까 샤딩 키로 쓴다”는 식보다는, 데이터가 균등하게 분포되도록 고려해야 합니다.

Hot Shard 대응

만약 필연적으로 증가형 값을 사용할 수밖에 없다면, 해시 샤딩(Hash Sharding)을 고려해 볼 수 있습니다. 이는 샤딩 키로 지정된 필드의 값을 해시 함수로 변환해 분산도를 높이는 방식으로, 일정 패턴 없이 고르게 데이터를 분산시키는 효과가 있습니다. 또 다른 방법으로는 샤딩 키를 복합적으로 구성해 특정 부분에 랜덤 요소를 추가하는 것도 생각해 볼 수 있습니다. 결국 핵심은 “특정 샤드에만 부하가 몰리지 않도록” 설계하고, Chunk Split과 Migration이 원활히 진행되도록 설정해 두는 것입니다.


샤딩 키가 데이터의 분포에 잘 맞아야, 각 샤드가 균등한 양의 읽기·쓰기를 처리하면서 높은 동시성을 유지할 수 있습니다. 반면 샤딩 키가 편중되면, 한쪽 샤드가 계속 락을 점유하고 이동 작업이 잦아지면서 성능 문제가 발생합니다. 따라서 MongoDB로 대규모 데이터를 처리하려면, 샤딩 키 선정과 Chunk 분할·이동 전략을 면밀히 설계하는 것이 무엇보다 중요합니다.


Sharded Cluster 환경에서 트랜잭션 처리 시 주의사항

MongoDB 4.2 이후로, 단일 Replica Set뿐 아니라 샤딩된(Sharded) 환경에서도 멀티 도큐먼트 트랜잭션을 사용할 수 있게 되었습니다. 이는 기존에는 한 샤드 내에서만 보장되던 원자성을 여러 샤드에 걸쳐 확장한 것이지만, 동시에 2PC(2-Phase Commit) 등 추가적인 복잡도가 생깁니다. 여러 샤드에서 커밋 또는 중단을 동기화해야 하다 보니, 네트워크 왕복 횟수와 잠금(락) 범위가 크게 늘어나며, 트랜잭션 내부의 문서가 많을수록 동시성 저하가 더 두드러지게 나타날 수 있습니다.


트랜잭션이 여러 샤드를 걸칠 때는 MongoDB가 자동으로 2PC 과정을 통해 각 샤드의 상태를 일관성 있게 맞춥니다. 하지만 이때, 각 샤드에 산재한 문서들이 하나의 트랜잭션으로 묶이게 되면서, 읽기·쓰기가 동시에 이루어지기 어렵고 대기 시간이 길어질 가능성이 큽니다. 따라서 가능하면 트랜잭션 범위를 최대한 축소하여, 각 샤드에 분산된 데이터를 한 번에 묶어 처리하기보다는 필요한 문서만 한정적으로 포함하는 방식이 권장됩니다. 이를 위해 스키마 설계를 재검토하거나, 일부 로직은 이벤트 기반으로 분리하는 방식을 생각해볼 수도 있습니다.


Sharded Cluster에서 트랜잭션 처리가 늘어나면, Mongos와 Config 서버의 역할이 매우 중요해집니다. 트랜잭션 정보를 각 샤드에 전달하고 커밋·중단을 조율하는 것은 Mongos가 담당하기 때문에, 트랜잭션이 빈번해지면 Mongos의 부하가 커질 수 있습니다. 또한 Config 서버가 Chunk 이동(Chunk Migration)을 진행 중이거나, 새 Shard를 추가하는 등의 메타데이터 변경 작업이 발생하면, 그 시점에 진행 중인 트랜잭션이 교착 상태나 지연 상태에 빠질 위험이 있습니다. 샤딩 키(Shard Key)로 지정된 필드가 트랜잭션 내에서 자주 변경된다면, Chunk Migration이 잦아질 수 있으므로 설계 단계에서 이 가능성을 미리 염두에 두는 것이 좋습니다.


Sharded Cluster 환경에서는 단일 Replica Set보다 모니터링해야 할 지표가 더 복잡합니다. Chunk 이동, Mongos 라우팅 지연, Config 서버 락 등 곳곳에서 잠금 경합(Contestion)과 병목이 발생할 수 있기 때문입니다. MongoDB가 제공하는 Diagnostic Commands나 MongoDB Atlas Performance Advisor 등의 도구를 활용하면, 어떤 샤드에 트래픽이 몰리는지, 트랜잭션 중 어떤 부분에서 시간이 오래 걸리는지를 한눈에 파악하기 유리합니다. 이를 토대로, Chunk를 재분할하거나 샤딩 키를 재설계하고, 트랜잭션 범위를 줄이는 등 다양한 최적화 조치를 취할 수 있습니다.

정리

샤딩 환경에서 동시성을 잘 유지하려면, 샤딩 키 선정과 Chunk 분할/이동 전략이 핵심입니다. 데이터가 고르게 분산되지 않으면 특정 샤드가 과부하를 받거나, Chunk Migration이 잦아지면서 성능과 동시성 모두 나빠질 수 있기 때문입니다. 또한 Cross-Shard Transactions를 무분별하게 사용하는 것은 쿼리 라우팅과 2PC 오버헤드로 이어져, 전체 클러스터의 성능 저하를 초래합니다.


따라서, 균등 분산 가능한 샤딩 키를 고르고, 트랜잭션은 짧게, 필요한 경우에만 사용하며, 청크 이동이나 스키마 변경 시 배포/배치 전략을 신중히 수립하는 것 이 Sharded Cluster에서 높은 동시성을 유지하는 핵심 포인트입니다.

참고 링크

MongoDB Manual: Sharding MongoDB Sharded Transactions Choosing a Shard Key