Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags more
Archives
Today
Total
관리 메뉴

seaking110 님의 블로그

N+1 문제 + 동시성 제어 본문

Today I Learned

N+1 문제 + 동시성 제어

seaking110 2025. 2. 24. 11:42

N + 1 문제란?

  • N + 1 문제는 ORM을 사용할 때 발생하는 성능 문제
    • 하나의 쿼리로 N개의 객체를 로딩한 후 각 객체에 연관된 데이터를 추가로 조회하는 개별 쿼리가 n번 싱핼 되면서 총 N + 1번의 쿼리가 발생하는 문제
    • 시스템의 확장성과 성능에 심각한 영향을 미칠 수 있는 구조적 문제
    • 요청이 동시에 여러 사용자로부터 발생한다면 데이터베이스는 수만개의 쿼리를 추가 처리!
  • FetchType.LAZY는 성능상의 이유로 사용해야 하지만 N+1 이 발생할 수 있음

예시

  • 모든 책을 조회하는 쿼리 1회 실행
  • 각 책에 대한 리뷰를 조회하는 N번의 추가 퀴리 실행
public void printBooksWithReviews() {
        // 모든 책을 조회하는 쿼리 1회 실행
        List<Book> books = bookRepository.findAll();

        // 각 책에 대한 리뷰를 조회하는 N번의 추가 쿼리 실행
        books.forEach(book -> {
            // 해당 책의 ID를 사용하여 리뷰를 조회
            List<Review> reviews = reviewRepository.findByBookId(book.getId());
          
        });
    }
  • 위 코드는 절대 짜면 안되는 N + 1 형식의 코드 

 

해결 방법

  • JOIN FETCH 사용
    • 관계가 있는 엔티티를 한 번의 쿼리로 함께 로드
    • 매우 추천하는 방법
    • 필요한 모든 데이터를 포함한 효율적인 쿼리를 생성하여 성능을 크게 향상!
    • inner join과 비슷하나 inner join은 데이터베이스 레벨 
  • 배치 사이즈 설정
    • 완전한 해결책은 아님
    • 하지만 성능 향상을 해줌
    • @BatchSize 어노테이션을 사용하여 한번에 로드할 연관 엔티티 수를 조정 가능
  • DTO 사용
    • 뷰나 API 응답으로 필요한 데이터만 선택적으로 전달
    • 불필요한 데이터를 로드하지 않아 성능을 향상
    • 매우 추천하나 근본적인 해결책은 아님
  • Entity Graphs 사용
    • 특정 쿼리에 대한 엔티티의 로딩 전략을 세밀하게 제어 가능
    • 유연성을 제공하므로 추천
    • JPA 2.1 부터 지원
    • @EntityGraph(attributePaths = {"reviews}) 처럼 사용
  • FetchType.EAGER 사용
    • 연관된 엔티티가 항상 필요한 경우 미리 로드하여 지연이 발생하지 않도록 함
    • FetchType.LAZY를 사용해야 성능이 뛰어나기 때문에 권장하지 않음
    • 너무 많은 데이터를 불필요하게 로드하므로 특히 많은 연관관계가 있는 경우 성능 저하를 초래

 

총정리

N+1 해결 방법 사용 사례 권장 여부 주요 특징 주의점
JOIN FETCH 관계가 있는 엔티티를 한번의 쿼리로 함께 로드 매우 추천 한번의 쿼리로 필요한 모든 데이터 로드 반환되는 데이터의 양이 많아짐
배치 사이즈 설정 대량의 연관 데이터를 로드 시 상황에 따라 선택 N+1 퀴리 수를 줄이나단지 성능 향상 적절한 배치 크기를 설정해야함
DTO 사용 뷰나 API 응답에 필요한 데이터만 전달 매우 추천 불필요한 데이터 로드 방지 데이터 변환 과정이 필요
Entity Graphs 특정 쿼리에서 필드 로드 방식을 제어 복잡한 경우 매우 추천 쿼리 세밀 제어 가능 복잡한 설정이 필요할 수 있음
FetchType.EAGER 연관된 엔티티가 항상 필요한 경우 권장 X 연관 렝니팉를 미리 로드하여 지연 없음 불필요한 데이터 로드로 성능 저하 가능성

 

 

 

동시성 제어

  • 락 이란
    • 주로 데이터베이스 관리 시스템이나 다중 스레딩 환경에서 사용하는 중요한 개념
    • 특정 자원에 대한 접근을 제어하여 동시성을 관리하고 데이터의 일관성 및 무결성을 유지하는 메커니즘
    • 자원 공유 : 여러 사용자나 프로세스가 동일한 데이터에 동시에 접근하려 할 때, 락은 이들 중 하나만이 데이터를 수정할 수 있도록 허용하여 데이터의 일관성을 보장
    • 데이터 무결성 : 데이터베이스에서 트랜잭션이 실행되는 동안 데이터 무결성을 유지하기 위해 락이 사용됨
  • 락의 종류
    • 공유 락
      • 데이터를 읽을 때 사용
      • 공유 락이 걸린 데이터는 다른 사용자도 읽을 수 있지만 수정은 할 수 없음
    • 독점 락

 

비관적 동시성 제어 (Pessimistic Concurrency Control)

  • 충돌이 발생할 것이라고 비관적으로 가정하고 데이터에 접근하기 전에 락을 사용하여 해당 데이터 보호
  • 주로 데이터베이스 트랜잭션이 길거나 충돌 가능성이 높을 때 사용
  • 데이터를 읽거나 수정하려는 동안 해당 데이터를 락을 걸고 다른 트랙잭션이 해당 데이터를 접근하는 것을 방지
  • 장점 : 데이터 무결성을 확실히 보장
  • 단점 : 락으로 인해 시스템의 처리 성능이 저하될 수 있으며, 데드락 발생 가능성이 있음

낙관적 동시성 제어 (Optimistic Concurrency Control)

  • 낙관적 동시성 제어는 충돌이 자주 발생하지 않는다고 낙관적으로 가정하고 트랜잭션이 데이터를 커밋할 때만 충돌을 검사
  • 주로 읽기 작업이 많고 쓰기 작업이 적을 때 적합
  • 데이터에 대한 락 없이 트랜잭션을 수행하고 커밋 시점에 변경 사항이 있는지 확인하여 충돌을 검사
  • 장점 : 동시성 수준이 높고 시스템의 처리 성능에 미치는 영향이 비교적 적음
  • 단점 : 충돌 발견 시 수행한 모든 작업을 롤백해야함

데드락 (DEADLOCK)

  • 두개 이상의 트랜잭션이 서로의 락을 기다리면서 무한 대기 상태에 빠지는 현상
  • 각 트랜잭션이 다른 트랜잭션이 소유한 자원을 요구할 때 발생
  • CPU 시간, 메모리, 파일, 장치 등의 자원에 대한 경쟁에서 발생

해결 방법

  • 예방
  • 데드락을 탐지하여 데드락에서 회복하기
  • 트랜잭션 진행방향을 같은 방향으로 처리
  • 트랜잭션 처리속도를 최소화
  • SET LOCK_TIMEOUT : 잠금 해제 시간 설정

 

'Today I Learned' 카테고리의 다른 글

Cookie, JWT  (0) 2025.02.26
테스트 코드!  (0) 2025.02.25
HttpMessageConverter란 무엇인가  (0) 2025.02.21
뉴스 피드 팀 프로젝트를 마치며  (0) 2025.02.20
[Git] Conventional Commits에 대해  (0) 2025.02.18