MongoDB의 Read Preference란?

테스트 환경에서는 보통 단일 서버로 구성된 Standalone 방식의 MongoDB를 사용하는 경우가 많다. 하지만 실제 운영 환경으로 들어가면 상황이 완전히 달라진다. 대부분의 서비스는 고가용성과 안정성을 확보하기 위해 Replica Set 구성이나 대량의 데이터를 처리하기 위한 Sharded Cluster 구조를 사용한다. 특히 일반적인 운영 토폴로지는 하나의 Primary와 두 개의 Secondary, 총 세 개의 멤버로 이루어진 Replica Set을 기본 단위로 삼는 경우가 흔하다.

이처럼 여러 노드가 동시에 존재하는 구조에서는, 애플리케이션이 어떤 멤버에서 데이터를 읽어야 할지 결정하는 문제가 자연스럽게 생긴다. Primary에 모든 읽기 요청이 집중되면 부하가 쏠려 전체 성능이 떨어질 수 있고, 반대로 Secondary를 적절히 활용하면 읽기 성능을 크게 개선할 수도 있다. 이런 상황에서 읽기 트래픽을 어떤 노드로 분산할지 선택하는 핵심 기능이 바로 MongoDB의 Read Preference다.

Read Preference Mode

MongoDB가 제공하는 Read Preference 모드는 primary, primaryPreferred, secondary, secondaryPreferred, 그리고 nearest 총 5가지이다. 이들은 각각 어떤 노드에서 데이터를 읽을지 결정하는 방식에 차이가 있으며, 서비스의 특성과 요구되는 일관성 수준에 따라 적절한 모드를 선택해 사용할 수 있다. 자세한 동작 방식은 아래에서 하나씩 살펴보겠다.

primary

primary 모드는 모든 읽기 트래픽을 Primary 노드로만 보내는 방식으로, MongoDB가 기본적으로 사용하는 Read Preference다. Replica Set 구조에서는 Primary에서 발생한 변경 사항이 Secondary로 전파되는 과정에서 복제 지연이 생길 수 있는데, 이 지연은 읽기 시점의 데이터 일관성에 영향을 줄 수 있다. 따라서 최신 데이터를 반드시 읽어야 하거나 지연이 없는 일관된 읽기가 중요한 상황에서는 primary 모드를 사용해야한다.

primaryPreferred

primaryPreferred 모드는 기본적으로 Primary를 우선적으로 사용하지만, Primary가 일시적으로 사용할 수 없는 상황에서는 자동으로 Secondary로 읽기 요청을 보낼 수 있도록 설계된 모드다. 일반적으로 애플리케이션이 Primary만을 대상으로 읽기를 수행하도록 설정해두면, Primary에 장애가 발생했을 때 읽기 요청 자체가 모두 실패하며 큰 장애로 이어질 수 있다. 이러한 위험을 줄이기 위해 MongoDB는 primaryPreferred 모드를 제공한다.

이 모드를 사용하면 정상적인 상황에서는 Primary에서 일관된 데이터를 읽을 수 있고, 만약 Primary가 잠시 내려가거나 선출 과정이 진행되는 동안에는 Secondary를 통해 읽기를 계속 이어갈 수 있어 서비스의 가용성을 높일 수 있다.

운영환경에서는 가능하면 primary 보다는 primaryPreferred 를 권장한다.

secondary

secondary 모드는 읽기 트래픽을 오직 Secondary 노드로만 보내도록 설정하는 모드이다. 이 모드를 사용하면 Primary는 쓰기 작업에 집중할 수 있고, 상대적으로 부담이 적은 Secondary 노드들이 읽기 요청을 처리하게 되어 전체적인 부하 분산에 큰 도움이 된다. 다만 Secondary는 Primary의 데이터를 복제하는 구조이기 때문에, 네트워크 지연이나 복제 지연이 발생하면 최신 데이터가 반영되지 않았을 가능성이 있다.

이러한 특성 때문에 secondary 모드는 추천 시스템이나 게시판 서비스 처럼 약간의 지연을 허용할 수 있는 상황에서 주로 활용된다.

secondaryPreferred

secondaryPreferred 모드는 기본적으로 Secondary 노드를 우선적으로 활용하지만, 사용할 수 있는 Secondary가 모두 unavailable 상태일 때는 Primary로 읽기를 수행하도록 하는 방식이다. 정상적인 상황에서는 Secondary로 읽기 트래픽을 보내 부하를 분산할 수 있고, Secondary에 문제가 생겼을 때도 읽기 자체가 중단되지 않도록 보완해주는 모드라고 볼 수 있다.

Replica Set의 세 멤버 중 두 개의 Secondary가 동시에 장애를 겪고 있다면, Primary가 모든 트래픽을 처리해야 하는 상황이 되어 이미 전체 Replica Set이 심각한 장애 상태일 가능성이 높다. 그럼에도 불구하고 최소한 읽기 요청이 완전히 중단되는 상황을 피하기 위해서는 secondary 모드 보다는 secondaryPreferred를 권장하지만, 서비스 특성에 따라 조절해서 사용하면 된다.

nearest

nearest라는 이름 때문에 가장 가까운 멤버 하나만을 선택해 읽기 요청을 보내는 방식으로 오해하기 쉽지만, 실제 동작 방식은 조금 더 복잡하다. MongoDB는 멤버 중 가장 빠른 RTT(Round-Trip-Time)에 Connection 옵션인 localThresholdMS (Default 15ms)를 더해 하나의 latency window를 만들고, 이 범위 안에 들어오는 멤버들을 모두 후보로 본다. 그리고 그 후보들 중에서 랜덤으로 노드를 선택해 트래픽을 라우팅한다.

정리하자면 nearestPrimary나 Secondary를 구분하지 않고, latency window 안에 포함되는 멤버들 중 하나를 무작위로 선택하는 방식이다. 결국 “가장 가까운 노드에 요청을 보낸다”는 설명은 틀린 것은 아니지만, 단일 노드가 아니라 일정 범위 안에서 가까운 노드들 사이로 트래픽이 분산된다는 점이 핵심이다. 이러한 특성 덕분에 같은 리전이나 AZ에 있는 서버보다 상대적으로 느린, 다른 리전의 Secondary에게도 자연스럽게 트래픽을 분배할 수 있다. 보다 깊은 내용은 별도로 정리한 Read Preference nearest 탐구 글에서 확인하자.

정리

MongoDB가 제공하는 Read Preference는 읽기 트래픽을 어떤 멤버로 보낼지 결정하기 위한 핵심 기능이며, 서비스 특성에 따라 성능과 일관성, 그리고 장애 대응 방식까지 좌우한다. 각 모드는 Primary 중심의 안정적인 읽기부터 Secondary 활용을 통한 부하 분산, 그리고 네트워크 지연을 고려한 최적 노드 선택까지 다양한 전략을 제공한다. 결국 중요한 것은 모든 상황에 맞는 단일한 정답이 있는 것이 아니라, 시스템의 목적과 요구사항에 가장 잘 맞는 읽기 전략을 선택해 최대한 활용하는 것이다.