Home HibernateCursorItemReader 사용 시 Out of Memory 발생 이유 및 해결 방법
Post
Cancel

HibernateCursorItemReader 사용 시 Out of Memory 발생 이유 및 해결 방법

HibernateCursorItemReader 사용 시 Out of Memory 발생 이유 및 해결 방법

Spring Batch에서 대용량 데이터를 처리할 때, HibernateCursorItemReader는 메모리 효율성을 높이기 위한 강력한 도구입니다. 그러나 올바르게 설정되지 않으면 OutOfMemoryError가 발생할 수 있습니다. 이번 글에서는 이 오류가 발생하는 원인과 해결 방법에 대해 자세히 설명하겠습니다.

Out of Memory 발생 이유

HibernateCursorItemReader를 사용할 때 Out of Memory 오류가 발생하는 주된 이유는 대용량 데이터를 메모리에 로드하기 때문입니다. 기본적으로, HibernateCursorItemReaderQuery.scroll() 메서드를 사용하여 결과를 가져오지만, 이를 사용할 때 적절한 설정이 없으면 메모리에 모든 결과를 로드할 수 있습니다.

useStatelessSession(true)를 설정하여 StatelessSession을 사용하더라도, JDBC 드라이버가 커서 기반으로 데이터를 가져오지 않으면 결국 모든 데이터를 메모리에 로드하게 되어 메모리 부족 문제가 발생할 수 있습니다.

해결 방법

Out of Memory 오류를 방지하기 위해 다음과 같은 방법을 사용할 수 있습니다:

  1. JDBC 드라이버의 커서 사용 설정: JDBC 드라이버가 커서 기반으로 데이터를 가져올 수 있도록 설정해야 합니다. MySQL의 경우 useCursorFetch 속성을 활성화할 수 있습니다. 예를 들어, HikariCP를 사용하는 경우 다음과 같이 설정할 수 있습니다:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
     @Bean
     public DataSource dataSource() {
         HikariDataSource dataSource = new HikariDataSource();
         dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
         dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
         dataSource.setUsername("username");
         dataSource.setPassword("password");
         dataSource.addDataSourceProperty("useCursorFetch", "true");
         return dataSource;
     }
    
  2. Fetch Size 조정: fetchSize를 설정하여 한 번에 가져오는 데이터의 양을 조절할 수 있습니다. 너무 작게 설정하면 성능이 떨어질 수 있고, 너무 크게 설정하면 다시 메모리 문제가 발생할 수 있습니다.

    1
    2
    3
    4
    5
    
     HibernateCursorItemReader<MyEntity> reader = new HibernateCursorItemReader<>();
     reader.setSessionFactory(sessionFactory);
     reader.setQueryString("FROM MyEntity");
     reader.setFetchSize(100);  // 적절한 fetchSize 설정
     reader.setUseStatelessSession(true);  // 메모리 효율성을 위해 StatelessSession 사용
    
  3. StatelessSession 사용: StatelessSession은 캐시를 사용하지 않기 때문에 메모리 사용을 줄일 수 있습니다. setUseStatelessSession(true)를 설정하여 StatelessSession을 사용하도록 합니다.

예제 코드

다음은 HibernateCursorItemReader를 적절하게 설정하여 대용량 데이터를 메모리 효율적으로 처리하는 예제입니다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Bean
public HibernateCursorItemReader<MyEntity> reader() {
    HibernateCursorItemReader<MyEntity> reader = new HibernateCursorItemReader<>();
    reader.setSessionFactory(sessionFactory);
    reader.setQueryString("FROM MyEntity");
    reader.setFetchSize(100);
    reader.setUseStatelessSession(true);
    return reader;
}

@Bean
public DataSource dataSource() {
    HikariDataSource dataSource = new HikariDataSource();
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
    dataSource.setUsername("username");
    dataSource.setPassword("password");
    dataSource.addDataSourceProperty("useCursorFetch", "true");
    return dataSource;
}

이 설정을 통해 데이터베이스 커서를 사용하여 한 번에 하나의 행씩 데이터를 가져오므로 메모리 사용량을 크게 줄일 수 있습니다.

결론

HibernateCursorItemReader를 사용할 때 Out of Memory 오류를 방지하려면 JDBC 드라이버의 커서 사용을 활성화하고, fetchSize를 적절하게 설정하며, StatelessSession을 사용하는 것이 중요합니다. 이를 통해 대용량 데이터를 처리할 때 메모리 효율성을 극대화할 수 있습니다.


참고 자료:

  1. Spring Batch Documentation
  2. Hibernate Documentation
  3. Spring HibernateCursorItemReader Examples
  4. GitHub Discussion on HibernateCursorItemReader Memory Issues
This post is licensed under CC BY 4.0 by the author.
Trending Tags