[Spring] 대용량 데이터 저장 시 OOM 문제 발생

Q0. 개요

설명에 앞서, 여기서 화자의 기준으로 작성된 JAVA는 ServiceImpl 비즈니스단을 말한다.

나의 경우에는 수십 만건의 대량의 데이터를 등록하는 데에 문제가 있었다. 

 

Q1. OOM (= Out of Memory)라는건 뭘까

시스템이 동작하기 위해 필요한 메모리가 부족하거나 없는 상태를 말한다.
⑴할당된 메모리가 부족 ⑵메모리가 계속 늘어나는 누수 (Leak) ⑶컨테이너의 메모리 제한이 없을때 발생.
해당 증상이 발생되면, 시스템 및 서버가 중단될 수 있는 크리티컬한 문제이다.

대게 본인의 경우, 대부분 1번이 주된 원인이였다.

 

- 첫번째는 실패

Mybatis에서 foreach를 이용하여 Insert ~ Value(), (), (), ......와 같은 방식으로 반복하여 등록하였다.

결론적으로는 JVM 힙 메모리가 초과하여 OOM이 발생하게 되었고, 힙 메모리 영역을 크게 잡아도 동일했다.

 

- 두번째는 실패.

자바에서 반복해서 Insert ~ Value()와 같은 방식으로 반복하여 등록하였다.

결론적으로는 커넥션 호출없이 아예 한번에 처리하는 위 방식보다 미세하게 나아졌나 싶을 정도로 증상은 여전했다.

위의 두 방식에는 Mybatis를 호출하는 선언문이 하나밖에 없었는데, 다른 JDBC 연결이 별도로 존재하지 않는 이상

SQL은 여전히 JVM의 힙 메모리에 적재되었다가 한번에 처리되는 것 같았다.

때문에 아래와 같이 변경할 수 밖에 없었다.

 

- Mybatis와 JDBC란

JDBC란,
DBMS를 만든 회사가 JDBC라는 인터페이스를 구현하여 DB Connection 기능에 필요한 드라이브를 제공한다.

Mybatis란,
JDBC를 더 쉽게 사용할 수 있도록 만들어주는 프레임워크이다.
직접 JDBC를 다루기보단 Mybatis를 통해 DB를 쉽게 관리할 수 있다.
(참고로 현재 대한민국을 비롯한 일본, 중국만 Hibernate보다 Mybatis의 사용량이 많다고 한다) 

 

- 세번째는 성공

동일한 기능이지만 선언구문을 분리시켜서 JDBC 연결을 강제로 끊고 다시 연결하는 작업을 생각하였다.

반복문에서 특정 대용량 횟수를 넘어가기 전에 조건을 통해 계속 분기해서 호출을 번갈아 하였더니 

OOM 증상은 아예 없어졌다. 

(참고로 Mybatis가 아닌 직접 JDBC를 작성했다면 PreparedStatement.executeBath를 이용하면된다)

그 외에도 Mybatis 내부적으로 가지고 있는 캐시와 관련된 방법도 있지만 본인의 경우에는 아래 런타임시 작동되는 Mybatis-config.xml 주요 환경 설정 중에서 defaultExecutorType을 BATCH 옵션만을 추가적으로 사용했다. 왜냐하면 캐시는 계속 새로이 등록되는 데이터가 OOM을 해결하는데에는 크게 효과가 없었기 때문이다.

 

Q2. 결론적으로

여러 구글링과 시행착오를 통해 얻은 결론은 오라클이 아닌 자바쪽에서 발생된 에러라면 힙 메모리 자체를 최대치로 올리거나 아니면 중간 중간에 끊어서 처리를 하는게 나은데, 대부분은 페이징 쿼리라고 정당량을 잘라서 저장하고 커밋하는 작업을 반복하는 방식이 좋다고 한다.