[트러블슈팅 - DB] Replica Lag 이슈

Hocaron·2023년 8월 14일
3

트러블슈팅

목록 보기
9/12


A 서버에서 Insert / Update 로직(Writer DB) 로직 수행 후에 B 서버에서 Select(Read DB) 로직이 수행되는 경우, Replica Lag 현상이 발생할 수 있다.

Replica Lag
복제 지연은 슬레이브(또는 보조)가 마스터(또는 기본)에서 발생하는 업데이트를 따라잡을 수 없을 때 발생한다. 적용되지 않은 변경 사항은 슬레이브의 릴레이 로그에 누적되며, 슬레이브에 대한 데이터베이스 버전은 마스터와의 차이는 점점 커진다.
즉, 슬레이브의 마스터 복제가 지연되는 것이다.

이를 방지하기 위해, A 서버에서 Read DB 에 데이터 복제가 잘 떠졌는지 확인 후 응답하는 로직을 추가하려고 한다.

readOnly = true 로 세팅하면 replica로 연결이 된다는 것을 알았으니, 이를 활용하여 replica 에 복제가 되었는지 확인한다.

Replica 에 복제가 잘 떠졌는지 확인하는 코드를 추가하자

코드는 다음과 같아요

    @Transactional
    public void save(long memberNo) {
        memberRepository.save(memberNo);
    }

    @Transactional(readOnly = true)
    public void checkReplica(long memberNo) {
        int retryCount = 1;

        while (retryCount <= 3) {
            log.info("[checkReplica] memberNo: {}, count: {}", memberNo, retryCount);
            if (memberRepository.findByMemberNo(memberNo).isPresent()) {
                return;
            }
            retryCount++;
        }
    }

save 로직이 수행된 후에 checkReplica 로직을 통해 최대 3번까지 replica DB 에 데이터가 복제되었는지 확인한다. 자가호출 방지를 위해 위 서비스로직들을 다른 클래스에서 호출하는 방식이다.

다른 트랜잭션인데, 왜 select 쿼리가 한번만 나가지?

분명히 다른 트랜잭션인데, 왜 save 로직에서 발생하는 select 쿼리 한번밖에 안 나갈까?!

spring.jpa.open-in-view:true

우리 서비스에서는 위 설정을 기본값인 true 로 두고 사용하고 있었는데, 이 설정에 따른 특징은 다음과 같다.

OSIV (Open Session In View) - true 설정

특징설명
영속성 컨텍스트 활성화DB 트랜잭션 시작 시 JPA 영속성 컨텍스트가 DB 커넥션을 가져옴
커넥션 유지@Transactional 메서드 벗어나도 커넥션을 계속 유지
지연 컨텍스트 유지응답 및 렌더링 시 영속성 컨텍스트 유지하여 지연 로딩 가능
실시간 트래픽 이슈커넥션 리소스 부족으로 인한 장애 가능성 증가
외부 API 호출 문제외부 API 호출 시 커넥션 리소스 반환 지연 문제
지연 로딩 활용 가능영속성 컨텍스트가 살아있어야 가능한 지연 로딩 활용

OSIV (Open Session In View) - false 설정

특징설명
영속성 컨텍스트 종료트랜잭션 종료 시 영속성 컨텍스트 닫고 DB 커넥션 반환
커넥션 최적화커넥션 리소스 낭비 없이 효율적으로 사용 가능
지연 로딩 제약모든 지연 로딩을 트랜잭션 내에서 처리해야 함
유연한 사용사용자 요청에 따라 커넥션을 유연하게 사용
지연 로딩 처리지연 로딩을 위해 트랜잭션 내에서 명시적으로 처리 필요

트랜잭션을 벗어나도 영속성 컨텍스트가 유지되는구나

해당 설정을 false 로 수정할 수 있지만, 어떤 사이드이펙이 있을지 모른다. 코드를 수정하자.

영속성 컨텍스트를 초기화해주자

코드는 다음과 같아요

    @Transactional
    public void save(long memberNo) {
        memberRepository.save(memberNo);
    }

    @Transactional(readOnly = true)
    public void checkReplica(long memberNo) {
        int retryCount = 1;

        while (retryCount <= 3) {
            log.info("[checkReplica] memberNo: {}, count: {}", memberNo, retryCount);
            entityManager.clear();
            if (memberRepository.findByMemberNo(memberNo).isPresent()) {
                return;
            }
            retryCount++;
        }
    }

checkReplica 내에 존재하는 select 쿼리가 매번 나가도록 영속성 컨텍스트를 초기화하는 코드를 추가하였다.

정리

  • Open Session In View 가 true 이면 영속성 컨텍스트가 유지된다.
  • Reader DB Connection 은 Write DB 에 붙은 후에 생성된다.
  • 이를 방지하기 이해 replication 설정으로 변경할 수 있다.

위 로직 및 설정을 추가하면 replica lag 은 해결되지만, 히스토리를 모르면 코드 파악이 어려워서 A 서버를 호출하는 B 서버에서 Write / Read DB 를 선택하는 코드를 추가하였다.

A 서버를 호출하는 경우에는 Write DB 를 선택하여 Replica Lag 현상이 일어나지 않도록 변경하였다.

profile
기록을 통한 성장을

0개의 댓글