대규모 트래픽을 다룰 수 있는 기회는 주로 큰 포털, 게임 업체 또는 급성장하는 스타트업과 같이 규모가 큰 기업들에 한정되어 있습니다. 그러나 대규모라 할지라도 구글이나 트위터와 같은 플랫폼과 비교하면 상대적으로 작을 것입니다. 최근에는 "대규모 트래픽"이라는 용어가 서비스가 처리할 수 있는 범위를 초과하는 트래픽이 몰린 상황을 가리키는 용어로 사용되는 것 같습니다. 서비스를 개발하다보면 수많은 문제가 발생할 것입니다. 저는 그러한 문제 중에서 쿼리 튜닝과 성능 개선 작업, 그리고 병목현상을 해결하는 작업에 많은 관심을 가지고 있습니다.
현재 진행 중인 사이드 프로젝트는 실제 서비스로는 이뤄지지 않지만, 트래픽이 몰릴 상황을 가정하며 개발하고 있습니다. 이 과정에서 데이터베이스에 마스터-슬레이브 구조를 적용하여 더 많은 트래픽을 처리할 수 있도록 개선한 과정을 공유해 보겠습니다.

해당 방식은 하나의 데이터베이스 서버에서 Read/Write 작업을 모두 처리하는 구조입니다. 이러한 구조는 초기 단계에서 간단하고 관리가 쉽지만, 대규모 트래픽을 처리하기에는 제약이 있을 수 있습니다.
더 많은 트래픽을 견디기 위해서 고려할 수 있는 방법에는 Scale Up과 Scale Out이 있습니다. 만약 기존 서버가 CPU: 4코어, RAM: 16GB, 스토리지: 1TB HDD로 서비스 하고 있었다면, Scale Up은 성능을 두 배 높이기 위해 CPU: 8코어, RAM: 32GB, 스토리지: 2TB HDD를 장착한 장비로 변경하는 것입니다. 즉 장비의 개수는 같지만 장비 하나의 성능을 두 배 높이는 것입니다. Scale UP은 기존의 아키텍처를 그대로 사용할 수 있기 때문에 돈만 있다면 가장 적용하기 쉬운 모델입니다.
반대로 Scale Out은 같은 장비를 두 대 사서 서비스에 적용하는 것을 의미합니다. 해당 방식은 여러 인스턴스를 추가하여 시스템의 성능을 향상시키는 방식입니다.
프로젝트가 서비스되면서 인기가 급증하여 사용자와 트래픽이 갑자기 증가하며, 이로 인해 DB에 부하가 걸리는 상황을 가정해 보겠습니다. 일반적인 서비스에서 Read/Write의 비율을 분석해보면 대략 7:3 혹은 8:2 정도로 Read 비율이 높습니다. 이는 일반적인 경우로 서비스 특성에 따라 다를 수 있지만, 서비스 이용 패턴을 분석하기에 제약이 있으므로 저는 8:2라고 가정하겠습니다.
위의 가정에 따르면 Read 작업을 분산시킴으로써 데이터베이스 서버의 부하를 감소시킬 수 있을 것입니다. 다시 말해, 하나의 Master 데이터베이스와 여러 개의 Slave 데이터베이스로 구성하여 Read와 Write를 다른 DB 서버로 분리하면 성능을 향상시킬 수 있을 것입니다. Read 작업에서 병목이 발생할 때마다 Read용 데이터베이스를 추가함으로써 데이터베이스의 Read 작업 부하를 낮출 수 있으며, 결과적으로 Master DB의 전체 부하를 감소시킬 수 있습니다.
Read를 분리함으로써 애플리케이션의 성능을 향상시킬 수 있지만, 데이터 일관성(Consistency) 측면에서 문제가 발생할 수 있습니다. 이런 상황을 설명하기 위해 진행 중인 커머스 서비스 프로젝트를 예로 들어보겠습니다. 예를 들어, 사용자 A가 판매 상품을 업로드했을 때, 해당 상품 정보가 상품목록 페이지에서 조회되지 않는 상황이 발생할 수 있습니다.
해당 문제의 원인은 데이터베이스의 리플리케이션 방식 때문입니다. 리플리케이션은 일반적으로 동기식과 비동기식으로 구분됩니다.
동기식 리플리케이션은 슬레이브가 데이터를 복제한 다음에 클라이언트에 응답을 주는 방식으로 작동합니다. 따라서 마스터와 슬레이브의 데이터는 항상 일치하지만, 처리 속도가 상대적으로 느립니다.
반면, 비동기식 리플리케이션은 응답이 클라이언트에 빠르게 전달되어 처리 속도가 빠르지만, 슬레이브에서 처리되기 전에 응답이 나갈 수 있습니다. 이로 인해 마스터와 슬레이브 간에 데이터 불일치가 발생할 수 있습니다. 또한, Multi Slave 구조의 경우 한 대는 리플리케이션이 빨리 완료되고 다른 대는 늦게 완료될 수 있으므로, 데이터를 가져올 때 어떤 데이터베이스에서 값을 가져오느냐에 따라 값이 달라질 수 있습니다.
이렇게 비동기식의 경우에서 리플리케이션 작업이 지연되더라도, 시간이 지나면 모든 데이터는 동일해집니다. 장애가 발생하지 않는 한, 모든 슬레이브는 언젠간 복제 작업이 완료될 것입니다. 이런 식으로 시간이 지나면서 데이터가 같아지는 것이 Eventaul Consistency 입니다. 따라서 데이터가 일치하지 않는 순간이 있더라도, 시간이 지나면 모두 일치하게 됩니다.
Multi Slave 구조를 활용하여 Read 작업을 분산하는 경우, 몇 대의 DB 서버가 필요한지 고민하게 되었습니다. 예산, 예상 트래픽, 응답 시간, 요구사항 등을 종합적으로 고려해야 하겠지만, 보통의 경우 최소한 3대의 데이터베이스 서버가 필요할 것으로 생각됩니다. 그 이유는 Slave 서버에 장애가 발생했을 때 대비해야 하기 때문입니다.
만약 Slave 서버에 장애가 발생했을 때 서버를 리부팅하여 문제를 해결할 수 있다면 다행일 것입니다. 그러나 최악의 경우인 디스크 고장과 같은 상황도 고려해야 합니다. DB 서버가 두 대만 있는 상황에서 한 대에 장애가 발생하면 남은 한 대로 서비스를 유지해야 하는 상황이 발생할 수 있습니다. 현재 트래픽이 집중되어 있는 DB에서 데이터를 복사하려고 하면 부하가 더 심해질 수 있습니다. 이로 인해 새로운 장비를 설정할 때 데이터를 복구하는 것이 어려워질 수 있습니다.
그러므로 최소한 세 대의 슬레이브 서버가 필요합니다. 이렇게 하면 한 대에 장애가 발생할 경우 다른 두 대로 서비스를 유지하고, 나머지 한 대를 사용하여 데이터 복구 작업을 진행할 수 있을 것입니다.
하지만 이는 대규모 트래픽을 처리하는 서비스의 경우이며, 비용 등의 고려 사항으로 인해 제 프로젝트에서는 Master DB 1대와 Slave DB 1대를 사용하였습니다.