본문 바로가기
trouble shooting

대용량 XML 데이터를 MyBatis로 삽입 시 GC Overhead Limit Exceeded 에러 해결 방법

by chunkind 2024. 5. 27.
반응형

대용량 XML 데이터를 MyBatis로 삽입 시 GC Overhead Limit Exceeded 에러 해결 방법

최근 프로젝트에서 대용량 XML 파일을 MyBatis를 통해 데이터베이스에 삽입하는 작업을 수행하던 중 GC Overhead Limit Exceeded 에러를 경험했습니다. 이는 Java 애플리케이션에서 가비지 컬렉터가 너무 많은 시간을 소비하고 있다는 경고입니다. 이번 포스트에서는 이 문제를 해결하기 위한 몇 가지 방법을 공유하려 합니다.

문제 원인

이 에러는 주로 메모리 부족이나 비효율적인 메모리 관리로 인해 발생합니다. 특히 대용량 데이터를 처리할 때 빈번하게 발생할 수 있습니다.

해결 방법

1. 배치 처리 (Batch Processing)

MyBatis는 배치 처리 기능을 지원합니다. 많은 수의 레코드를 한 번에 삽입하여 메모리 사용을 최적화할 수 있습니다. 아래는 배치 처리를 위한 코드 예제입니다:

SqlSessionFactory sqlSessionFactory = ...; // Your SqlSessionFactory configuration
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
    MyMapper mapper = session.getMapper(MyMapper.class);

    for (int i = 0; i < dataList.size(); i++) {
        mapper.insertData(dataList.get(i));
        if (i % 1000 == 0 || i == dataList.size() - 1) { // Commit every 1000 records
            session.commit();
            session.clearCache(); // Clear the session cache to free up memory
        }
    }
}

2. 메모리 튜닝 (Memory Tuning)

JVM 옵션을 조정하여 메모리 사용을 최적화할 수 있습니다. 아래는 JVM 힙 메모리를 증가시키고, 가비지 컬렉션 설정을 조정하는 방법입니다:

java -Xms4g -Xmx8g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump

여기서:

  • -Xms4g-Xmx8g는 초기 및 최대 힙 메모리 크기를 설정합니다.
  • -XX:+UseG1GC는 G1 가비지 컬렉터를 사용하도록 설정합니다.
  • -XX:MaxGCPauseMillis=200은 최대 GC 중단 시간을 설정합니다.
  • -XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath는 OutOfMemoryError 발생 시 힙 덤프를 생성하도록 설정합니다.

3. 스트리밍 처리 (Streaming Processing)

XML 파일을 한 번에 메모리에 로드하지 않고 스트리밍 방식으로 처리하면 메모리 사용량을 줄일 수 있습니다. 예를 들어, StAX (Streaming API for XML)을 사용하여 XML 데이터를 스트리밍 방식으로 읽어들일 수 있습니다.

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.events.XMLEvent;
import java.io.InputStream;

XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
try (InputStream inputStream = new FileInputStream("path/to/your/large.xml")) {
    XMLEventReader reader = xmlInputFactory.createXMLEventReader(inputStream);

    while (reader.hasNext()) {
        XMLEvent event = reader.nextEvent();
        // Process each event, e.g., start element, end element, character data
        // Convert the event to your data model and add to batch for MyBatis insert
    }
}

4. 데이터베이스 튜닝

데이터베이스 설정을 최적화하여 대량 삽입 성능을 개선할 수 있습니다. 예를 들어, 인덱스를 비활성화하거나, 대량 삽입 중에 자동 커밋을 비활성화하는 방법이 있습니다.

5. 병렬 처리 (Parallel Processing)

데이터를 여러 스레드로 분할하여 병렬로 삽입하면 전체 처리 시간을 줄일 수 있습니다. Java의 ForkJoinPool 또는 ExecutorService를 사용할 수 있습니다.

ExecutorService executorService = Executors.newFixedThreadPool(4); // Number of threads
List<Future<?>> futures = new ArrayList<>();

for (List<Data> partition : partitions) {
    futures.add(executorService.submit(() -> {
        try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
            MyMapper mapper = session.getMapper(MyMapper.class);
            for (Data data : partition) {
                mapper.insertData(data);
            }
            session.commit();
        }
    }));
}

for (Future<?> future : futures) {
    future.get(); // Wait for all tasks to complete
}
executorService.shutdown();

이와 같은 방법들을 조합하여 GC Overhead Limit Exceeded 문제를 해결할 수 있습니다. 데이터 크기와 시스템 환경에 따라 적절한 방법을 선택하여 사용하세요.


반응형