목록으로

Programming Notes

JPA Batch Insert 성능 저하의 함정: GenerationType.IDENTITY의 비밀

JPA를 사용하여 대량의 데이터를 저장하는 작업을 진행하다 보면, 예상치 못한 성능 저하에 직면할 수 있습니다. 특히 GenerationType.IDENTITY 전략을 사용하는 경우 더욱 그렇죠. MySQL의 AUTO_INCREMENT 와 자연스럽게 연결되는 IDENTITY...

JPA를 사용하여 대량의 데이터를 저장하는 작업을 진행하다 보면, 예상치 못한 성능 저하에 직면할 수 있습니다. 특히 GenerationType.IDENTITY 전략을 사용하는 경우 더욱 그렇죠. MySQL의 AUTO_INCREMENT와 자연스럽게 연결되는 IDENTITY 전략은 편리하지만, saveAll()과 같은 벌크 저장 함수를 사용할 때 치명적인 단점을 드러냅니다. 이 글에서는 GenerationType.IDENTITY 전략이 벌크 인서트 성능에 어떤 영향을 미치는지, 그리고 그 이유를 명확하게 설명하고자 합니다.

JPA는 saveAll() 메서드를 통해 여러 엔티티를 한꺼번에 영속화할 수 있도록 지원합니다. 하지만 GenerationType.IDENTITY를 사용하면 이 기능이 제대로 동작하지 않는다는 사실을 알아야 합니다. IDENTITY 전략은 데이터베이스가 기본키를 자동으로 생성하도록 위임하는 방식입니다. 문제는 JPA가 saveAll()을 호출하면, 각 엔티티에 대해 개별적인 INSERT 쿼리를 실행한다는 점입니다. 예를 들어, 10만 개의 엔티티 목록을 saveAll()로 저장하려 한다면, 무려 10만 개의 INSERT 쿼리가 데이터베이스로 전송됩니다. 이는 데이터베이스에 상당한 부하를 주어 성능 저하의 주요 원인이 됩니다. 왜 이런 일이 발생할까요? 그 이유는 JPA가 IDENTITY 전략을 사용할 때 각 엔티티의 기본키 값을 미리 알 수 없기 때문입니다. 데이터베이스가 기본키를 생성하기 전까지는 JPA는 INSERT 쿼리를 실행할 수 없습니다. 따라서, 각 엔티티마다 개별적으로 데이터베이스에 접근하여 기본키를 할당받고, 그 후에 INSERT 쿼리를 실행하는 과정을 거치게 되는 것입니다.

이러한 성능 저하 문제를 해결하기 위해서는 GenerationType.IDENTITY 대신 다른 기본키 생성 전략을 사용하는 것을 고려해야 합니다. GenerationType.TABLE이나 GenerationType.SEQUENCE와 같은 전략은 미리 기본키 값을 생성하여 JPA가 벌크 인서트를 효율적으로 수행할 수 있도록 지원합니다. 또한, JPA의 벌크 업데이트 기능을 활용하거나, JDBC를 직접 사용하여 벌크 SQL 쿼리를 실행하는 것도 성능 향상에 도움이 될 수 있습니다. 데이터의 특성과 데이터베이스 환경에 맞는 최적의 전략을 선택하는 것이 중요합니다. 단순히 편리함 때문에 GenerationType.IDENTITY를 고집하기보다는, 벌크 저장 시 성능에 미치는 영향을 먼저 고려하고, 필요에 따라 적절한 전략을 선택하여 개발해야 합니다. 결국, 성능 최적화는 개발 단계에서부터 신중한 설계와 선택을 통해 이루어져야 하는 과정입니다.