DB Replication 복제 지연 해결

정정일·2024년 11월 15일
0

이번에 회사에서 직면했던 복제지연 문제에 관한 내용을 공유하고자 글을 남깁니다.


문제 발생

저희 회사에서는 database replication을 통해 부하를 분산하여 성능을 높히는 방식을 사용하고 있습니다.

image

저희는 Master와 Slave 구조의 Active Active 이중화로
Proxy sql을 통해 SELECT는 Slave로 INSERT, UPDATE, DELETE는 Master로 SQL이 날라가게 되고 변경된 데이터가 Slave로 동기화되는 작업이 이루지게 됩니다.

image

이러한 부하분산을 통해 성능을 향상시켜왔습니다.

위와같은 구조에서 발생할 수 있는 문제로 가장 먼저 이야기가 나올만한 부분은 복제 지연문제입니다.

그림으로 설명드리는게 빠르겠네요

image

위처럼 INSERT는 Master로 날라가게 되고 SELECT는 Slave로 날라가게 됩니다. 문제는 데이터베이스간 싱크가 비동기적으로 이루어지니 사용자가 INSERT를 한 직후 SELECT를 하게되면 Slave에 데이터 동기화가 완료되지 않은 시점이라면 사용자 입장에서는 데이터를 유실한것 처럼 보여지게 됩니다.


(관련된 문제에서 발생한 Exception Slack)


해결방법

저희 회사에서는 위 문제가 꾸준히 있어왔는데 위 문제를 해결하는 방법에는 다양한 방법이 있습니다. 예를 들어 자신이 쓴 내용 읽기, 단조 읽기, 일관된 순서로 읽기 등이 있습니다.

그러나 저희 회사가 위 문제를 해결하기 위해 지금까지 선택해왔던 방법은 생각보다 간단하고 무식한 방법이였는데요.

주요 비지니스를 다루는 어플리케이션은 ProxySQL이 아닌 Master만 보도록 한다.

였습니다.

위 해결방법으로 해결했었던 이유는 크게 두가지였습니다.

1. 일시적인 문제로 데이터 동기화가 이루어지면 더이상 발생하지 않는다.
2. 주요 비지니스 로직은 INSERT 후 SELECT 하더라도 문제가 없도록 하면 된다.
3. 다른 개발일정이 있기때문에 위 문제에 디버깅, 개발할 여유가 되지 않는다.

이러한 3가지 이유로 위 해결방법이 선택됐었습니다.
그렇지만 위 해결방법은 database replication을 통해 부하를 분산하여 성능을 높히는 방식과는 거리가 멀어지는 해결방법이였기 때문에 좀더 나은 해결방법이 있다면 해결하고 싶다는 욕구는 항상 가지고 있었고 이번에 개발 일정을 주어졌던 일정보다 빨리 끝낸 제가 맡아서 해보겠다 건의드렸습니다.

승인은 어렵지 않게 떨어졌고 아래 Reference들을 찾아보며 어떤 방식으로 해결하는 것이 좋을지 고민하게 됐습니다.

제가 선택한 방법은 AbstractRoutingDataSource를 통해
INSERT후 SELECT해야할만한 로직이 포함된 곳이라면 Master를 바라볼 수 있도록 구현하는 방법이였습니다. (관련글1, 관련글2)

적용하는 방법은 위글에 자세히 나와있으니 다루지는 않겠습니다.

위를 저희회사 모든 어플리케이션에 구현하고 INSERT후 SELECT해야할만한 로직이 포함된 곳이라면 어노테이션을 통해 Master를 바라볼 수 있도록 했습니다.

ProxySQL의 transaction_persistent=1 옵션이 켜져있었기 때문에 해당하는 로직에 Transaction만 도입한다 하더라도 Transaction이 존재하는 곳은 Master만 바라보게 되겠지만 개별적으로 Insert가 이루어져야할 로직들이 모여있는 곳들도 있었기에 고려만 했었습니다.

그러나 여전히 문제는 발생할 수 있습니다.
사용자 A가 INSERT한 데이터를 사용자 B가 거의 동시에 읽으려 시도할 경우입니다.

하지만 이와같은 케이스는 빈번하지 않을 것이라 생각했고 위 방법을 모든 어플리케이션에 적용했다 하더라도 여전히 주요 비지니스를 다루는 어플리케이션은 ProxySQL이 아닌 Master만 보도록 뒀기 때문에 end user 입장에서는 이슈를 느끼지 못할 가능성이 높았습니다.


사실 복제지연 문제는 Replication 구조를 가지고 있거나 서로 다른 DB간 데이터 복제가 이루어져야할 때 맞이할 수 밖에 없는 고질적인 문제라고 생각합니다.

항상 나중에 해결해야지 해결해야지라는 생각으로 미뤄왔지만 이번에 NAVER 컨퍼런스인 DAN24에 다녀오면서 NAVER FINANCIAL Tech에 김진한님 세션 네이버페이 결제 시스템의 성장과 변화를 듣고 아 나도 회사 돌아가면 복제지연 문제를 해결해보고 싶다라는 생각이 들어 해결하게 됐습니다.

굉장히 좋은 세션과 발표였습니다.

위 본문 내용중 정확하지 않은 내용이 포함돼 있을 수 있습니다.
저는 2년차 백엔드 개발자로 스스로 굉장히 부족한 사람이라는 점을 인지하고 있는지라
제가 적은 정보가 정확하지 않을까 걱정하고 있습니다.
혹여 제 정보가 잘못 됐을 수 있으니 단지 참고용으로만 봐주시고 관련된 내용을 한번 직접 알아보시는 걸 추천합니다.
혹여 잘못된 내용이 있거나 말씀해주시고 싶은 부분이 있으시다면 부담없이 적어주세요!
수용하고 개선하기 위해 노력하겠습니다!

Reference

https://drunkenhw.github.io/java/select-database/
https://velog.io/@ghkvud2/AbstractRoutingDataSource-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0
https://velog.io/@kimtjsdlf/%EB%B3%B5%EC%A0%9C2-%EB%B3%B5%EC%A0%9C-%EC%A7%80%EC%97%B0

DB Replication — 복제 지연 문제 해결

이번 글에서는 회사에서 직면했던 데이터베이스 복제 지연 문제와 이를 해결한 방법에 대해 공유하고자 합니다.


해당 문제 발생

현재 회사에서는 데이터베이스 복제를 통해 부하를 분산하여 성능을 향상시키고 있습니다.

Replication Structure

시스템은 Master-Slave 구조의 Active-Active 이중화로 구성되어 있으며, ProxySQL을 통해 SELECT 문은 Slave로, INSERT, UPDATE, DELETE 문은 Master로 전송됩니다. 이후 Master에서 변경된 데이터는 Slave로 동기화됩니다.

이 구조를 통해 부하를 분산하고 성능을 향상시키고 있었으나, 여기서 종종 발생하는 문제 중 하나가 바로 "복제 지연 문제"입니다.

Replication Lag Issue

예를 들어, 사용자가 INSERT 작업을 수행한 직후에 SELECT를 호출할 때, Slave에 데이터 동기화가 완료되지 않았다면 사용자 입장에서는 데이터가 누락된 것처럼 보일 수 있습니다.

Exception Example

위 예시는 관련 문제로 발생한 예외 메시지가 Slack에 전송된 화면입니다.


해결 방법

회사는 복제 지연 문제를 해결하기 위해 여러 가지 방법을 고민했습니다. 일반적으로 고려할 수 있는 해결 방안은 다음과 같습니다.

  • 자신이 쓴 데이터 읽기(Read-Your-Own-Write)
  • 단조 읽기(Monotonic Reads)
  • 일관된 순서로 읽기(Consistent Reads)

하지만, 저희 회사가 처음에 선택한 방법은 간단하고 직관적인 방식이었습니다.

주요 비즈니스 로직을 다루는 애플리케이션의 경우 ProxySQL을 사용하지 않고 Master만 바라보도록 한다.

이 방법을 채택한 이유는 크게 세 가지였습니다.

  1. 일시적인 문제로, 데이터가 동기화되면 더 이상 문제는 발생하지 않기 때문.
  2. 주요 비즈니스 로직에서는 INSERT 후 SELECT가 필요한 경우가 많아 문제 발생 가능성을 낮출 수 있었기 때문.
  3. 다른 개발 일정이 우선시되었기 때문에 추가적인 디버깅 및 개발 시간을 절약해야 했기 때문.

이 방법은 복제를 통한 부하 분산이라는 목표와는 다소 거리가 있었지만, 당장의 문제를 해결하기 위해서는 현실적인 대안이었습니다.


더 내용적인 해결 방법 고민 및 적역

더 나은 해결책에 대한 필요성을 느끼고 있었던 저는, 일정에 여유가 생기자 이 문제를 직접 해결해보겠다고 자청했습니다. 관련 자료를 찾아보며 참고한 주요 레퍼런스는 다음과 같습니다.

이 자료들을 바탕으로 AbstractRoutingDataSource를 사용하여 해결하는 방법을 도입했습니다. 이 방식을 통해, INSERTSELECT 작업이 필요할 수 있는 로직이 포함된 부분에서는 Master를 바라보도록 구현했습니다.

적용 방법

  1. AbstractRoutingDataSource, AOP 적용:

    • INSERTSELECT 작업이 필요한 부분을 구분해, 해당 로직 내에서는 Master를 바라보도록 설정했습니다.
  2. 트랜잭션 도입:

    • ProxySQL을 사용하는 경우 트랜잭션 내에서는 기본적으로 Master만 바라보게 됩니다. 이를 활용하여, 개별적인 INSERT 작업이 이뤄지는 곳에서도 자연스럽게 Master를 바라보도록 했습니다.
  3. 주요 비즈니스 로직에만 Master 사용:

    • 일부 주요 비즈니스 애플리케이션의 경우 ProxySQL을 거치지 않고 Master에만 직접 연결하도록 했습니다.

이 방식은 여전히 몇 가지 문제의 소지를 남겼습니다. 예를 들어, 사용자가 A와 B가 거의 동시에 데이터를 삽입하고 조회하는 경우, A가 INSERT한 데이터를 B가 바로 조회하려고 할 때 문제가 발생할 수 있습니다. 그러나 이러한 케이스는 빈번하지 않을 것이라고 판단했습니다.

또한, 주요 비즈니스 로직은 Master에만 연결해 두었기 때문에 사용자 입장에서 문제를 느낄 가능성은 매우 낮았습니다.


느낀 점

사실 복제지연 문제는 Replication 구조를 가지고 있거나 서로 다른 DB간 데이터 복제가 이루어져야할 때 맞이할 수 밖에 없는 고질적인 문제라고 생각합니다.

항상 나중에 해결해야지 해결해야지라는 생각으로 미뤄왔지만 이번에 NAVER 컨퍼런스인 DAN24에 다녀오면서 NAVER FINANCIAL Tech에 김진한님 세션 네이버페이 결제 시스템의 성장과 변화를 듣고 아 나도 회사 돌아가면 복제지연 문제를 해결해보고 싶다라는 생각이 들어 해결하게 됐습니다.

굉장히 좋은 세션과 발표였습니다.

위 본문 내용중 정확하지 않은 내용이 포함돼 있을 수 있습니다.
저는 2년차 백엔드 개발자로 스스로 굉장히 부족한 사람이라는 점을 인지하고 있는지라
제가 적은 정보가 정확하지 않을까 걱정하고 있습니다.
혹여 제 정보가 잘못 됐을 수 있으니 단지 참고용으로만 봐주시고 관련된 내용을 한번 직접 알아보시는 걸 추천합니다.
혹여 잘못된 내용이 있거나 말씀해주시고 싶은 부분이 있으시다면 부담없이 적어주세요!
수용하고 개선하기 위해 노력하겠습니다!


Reference

https://drunkenhw.github.io/java/select-database/
https://velog.io/@ghkvud2/AbstractRoutingDataSource-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0
https://velog.io/@kimtjsdlf/%EB%B3%B5%EC%A0%9C2-%EB%B3%B5%EC%A0%9C-%EC%A7%80%EC%97%B0

profile
하루하루 더 발전하는 사람이 되기 위해 노력하는 개발자가 되고자 합니다.

0개의 댓글