Replication Lag?
현대 많은 대규모 서비스들에선 단일 장애 포인트를 없애고 서비스에 일관된 수준의 빠른 응답을 보장하기 위해 데이터베이스를 Master-Slave 구조의 Replication 형태로 구축한다.
서비스에서 발생하는 모든 데이터 변경 작업(CUD)을 Master 데이터베이스에서 처리하도록 하고, 처리완료된 데이터를 Slave 데이터베이스들에 싱크하는 구조이다.
위와 같은 구조를 통해 Master 데이터베이스 장애 시 Slave 데이터베이스를 통한 Failover 전략을 가능하게 하고, Slave 데이터베이스의 Scale Out을 통해 서비스 쿼리의 대부분을 차지하는 조회 작업을 일관된 품질로 제공할 수 있도록 해준다.
그러나 동시에 데이터의 일관성이 깨지는 케이스가 발생하기도 하는데 그것이 이번 글에서 서술할 복제 지연문제(Replication Lag)이다.
일반적으로 사용되는 MySQL의 비동기 복제는 아래와 같은 플로우로 이루어진다.
Source(Master)에 데이터 변경이 발생하면 작업 내용을 binlog에 저장한 후 커밋을 하게 되는데, 해당 Source를 바라보고 있는 Replica(Slave)는 해당 binlog를 받아와 relay log에 기록하고 동일한 순서로 쿼리를 실행시켜 자신의 데이터베이스에 반영하게 된다.
해당 작업은 일반적으로 수십 밀리세컨즈 내에 완료될 정도로 빠르게 완료되지만, 레플리카의 개수가 지나치게 많아지거나 소스와 레플리카 간 네트워크 연결이 불안정하거나 혹은 MySQL 서버의 스펙이 좋지않는 등의 이유로 일정 시간 복제가 지연될 수 있다.
이로 인해, 아래와 같이 방금 쓴 데이터를 바로 조회하지 못하는 등의 데이터 일관성 문제가 발생할 수 있다.
해결 전략
Replication Lag을 해결하기 위해 데이터베이스 레벨 혹은 애플리케이션/플랫폼 레벨에서 다양한 전략을 취해볼 수 있다.
해당 글은 Read-your-write consistency 라는 제목의 포스트에 소개된 전략을 재정리하여 작성되었다.
1. 동기 방식 복제
가장 간단하고 확실한 방법은 Source - Replica 간 싱크 작업을 비동기가 아닌 동기 방식으로 처리하는 것이다.
복제 지연 문제가 발생하는 근본적인 이유는 두 데이터베이스 간 싱크 작업이 비동기로 이루어진다는 것에 있다.
만약 소스 데이터베이스의 데이터 수정 작업이 레플리카 데이터베이스의 싱크작업이 모두 완료된 후에 순서대로 커밋되는 형태를 갖춘다면 사용자들은 언제 데이터를 조회하더라도 항상 최신의 데이터를 받아볼 수 있다.
그러나 이는 곧 DML의 총 수행시간이 쿼리 수행시간 + 싱크 수행시간이 됨을 의미하고, 그만큼 쓰기 성능을 저해할 수 있다.
또한 Slave가 늘어날수록 쓰기 성능 저해가 심해지므로, 마음껏 Scale Out을 할수도 없는 상황에 놓인다.
대규모 서비스의 운영환경에 취하기엔 다소 부담스러운 전략이다.
2. 사용자 핀 방식 (Pinning User to Master)
해당 방식은 항상 최신 데이터를 유지하는 Master 데이터베이스를 한시적으로 읽기 작업에 사용하는 방식이다.
복제 지연으로 인해 사용자 경험이 저해되는 케이스는 일반적으로 "사용자가 방금 삽입/수정한 데이터를 최신 버전으로 업데이트받지 못할 때"이다.
SNS를 예로 들어, 만약 사용자가 방금 막 게시글을 업로드했음에도 자신의 피드를 새로고침했을 때 해당 게시글이 노출되지 않는다면 이상하다고 느낄 것이다.
이와 같은 문제를 해결하기 위해 CUD 작업을 수행한 사용자에 대하여 일정 시간동안 앞으로의 요청을 Master 데이터베이스로 처리하도록 할 수 있다.
다른 사용자들에겐 복제 지연으로 인한 이슈가 여전히 발생할 순 있지만 그것이 사용자 경험을 저해하진 않는다.
하지만 해당 방식은 CUD 작업이 빈번하게 일어나는 서비스라면 그만큼 Master 데이터베이스를 사용하게 되는 사용자가 늘어나게 될 것이고 이는 레플리카를 사용해 쿼리를 부하분산하려는 의미를 퇴색시킬 수 있다.
3. 부분 핀 방식(Fragmented Pinning)
부분 핀 방식은 사용자 핀 방식의 문제점을 보완하기 위한 전략이다.
CUD 작업을 한 사용자의 이후 요청을 일정시간동안 Master 데이터베이스에서 처리하도록 한다는 컨셉은 같으나, 그 범위를 특정 데이터로 한정짓는 것이다.
동일하게 게시글을 작성한다고 했을 때 오로지 게시글에 대해서만 Master 데이터베이스를 바라보게 하고, 그 외 다른 읽기 작업은 Slave 데이터베이스에서 처리하도록 하는 방식이다.
위와 같은 방식은 주요 데이터들에 대한 일관된 데이터 읽기 작업을 지원함과 동시에 Master 데이터베이스로 지나치게 많은 요청이 몰리지 않도록 하는 효과를 볼 수 있다.
4. Master Fallback
해당 방식은 이름에서 유추할 수 있듯 Master 데이터베이스를 Slave 데이터베이스의 Fallback으로 사용하는 방식이다.
필요한 데이터를 우선 Slave 데이터베이스에서 조회해본 후, 데이터 조회에 실패했다고 판단하면 Master 데이터베이스에 조회를 시도하는 방식이다.
위의 핀 방식과 비교했을 때 적용 가능한 상황이 다소는 제한적으로 보인다.
일반적으로 DML이 INSERT 작업일 때 사용 가능한 방식이며, UPDATE 작업일 땐 업데이트 이전의 데이터를 따로 보관하는 등의 추가적인 작업이 필요할 것으로 보인다.
Reference
https://arpitbhayani.me/blogs/read-your-write-consistency
https://dev.mysql.com/doc/refman/8.4/en/
'그 외 공부 > 기타' 카테고리의 다른 글
Static Method를 Mocking 하게 해줘야할까? (0) | 2024.10.24 |
---|---|
[Gradle] settings.gradle에 멀티 프로젝트 설정하기 (0) | 2023.06.09 |
메일 서버의 동작 구조와 프로토콜 (SMTP, POP3, IMAP) (0) | 2022.08.05 |
리눅스 크론탭(crontab) 사용법 (0) | 2021.07.31 |