본문 바로가기
JPA

JPA - save() & saveAll() 성능 비교 분석

by devLog by Ronnie's 2023. 5. 27.

JPA를 사용하면 데이터를 저장할 때 save와 saveAll 함수를 사용하여 데이터를 저장할 수 있다. 두가지 함수의 차이는 데이터를 한번에 한 개를 저장하느냐 N개를 저장하느냐의 차이다. 만약 10개를 데이터를 저장해야 될 때 save()를 통해 데이터를 10번 저장하는 것과 saveAll()을 통해 한번에 데이터를 저장하는 것이 성능적으로 어떻게 차이가 있을지에 대한 궁금점을 해결하기 위한 글이다.

 

JPA - save() & saveAll() 성능 비교 분석

 

save() vs saveAll() 성능 비교 분석

 

save() vs saveAll() 성능 테스트


거두절미하고 바로 N개의 데이터에 대하여 save()와 saveAll()에 두가지 함수에 대해서 성능을 측정해본다. 성능 테스트 코드는 아래 코드를 참고하자.

    @Test
    @Transactional
    fun `save() 시간 측정 테스트`() {
        val start = System.currentTimeMillis()

        var count = 300000

        while (count-- > 0) {
            val member1 = Member1(name = "test", age = 20)
            memberRepository.save(member1)
        }

        println("시간 측정 결과 : " + (System.currentTimeMillis() - start) + "ms")
        /**
        데이터 10만건 -> 7.8초
        데이터 30만건 -> 16.1초
         */
    }

    @Test
    @Transactional
    fun `saveAll() 시간 측정 테스트`() {
        val start = System.currentTimeMillis()

        var count = 300000

        val members = mutableListOf<Member1>()

        while (count-- > 0) {
            val member1 = Member1(name = "test", age = 20)
            members.add(member1)
        }

        memberRepository.saveAll(members)

        println("시간 측정 결과 : " + (System.currentTimeMillis() - start) + "ms")
        /**
         데이터 10만건 -> 6.0초
         데이터 30만건 -> 10.3초
         */
    }

 

성능 테스트의 기준은 10만건 / 30만건의 데이터를 동일한 조건하의 저장한다.

 

10만건 저장 테스트

save() -> 소요 시간 7.8초

saveAll() -> 소요 시간 6초

saveAll()이 좀 더 빠르나 아주 큰 차이를 보이지는 않음.

 

30만건 저장 테스트 

save() -> 소요 시간 16.1초

saveAll() -> 소요 시간 10.3초

저장하는 데이터가 늘어나니 save()와 saveAll()의 성능 차이를 좀 더 체감할 수 있다. 즉, 데이터가 많아지면 많이질수록 save()보다는 saveAll()이 성능측면에서 더 유리하다는 것을 알 수 있다.

 

그럼 어떤 이유때문에 이렇게 성능 차이가 나는 것일까?

 

 

성능 차이 이유


성능 차이의 이유를 알고 싶다면 먼저 save()와 saveAll()의 구현 코드를 보면 알 수 있다.

 

아래는 save()이다

다음은 saveAll() 이다. saveAll()도 내부적으로 this.save()를 통해 save() 함수를 호출한다.

그래서 찍히는 로그를 보면 saveAll()로 아래와 같이 insert 쿼리가 여러 번 발생하는 것을 확인할 수 있다.

 

 

그렇다면 saveAll()로 save()를 호출하는데 왜 성능 차이가 나는 것일까?


이유는 바로 Transaction에 있다.

 

save()로 저장 시 1건의 데이터 마다 save()를 호출하게 된다.

saveAll()은 1건 마다 인스턴스 내부의 save()를 호출하게 된다.

 

이러한 차이가 성능상의 차이를 만들게 되는데 먼저 save() 호출 시 상위에 Transaction이 존재하는 경우에는 해당 Transaction에 참여하게 되고 없다면 새로 Transaction을 생성하여 save 후 커밋을 하는 과정을 거치게 된다.

 

이러한 과정에서 만약 상위의 Transaction이 있다하더라도 외부 Bean(Repository) 객체의 save()를 호출하게 되므로 비용이 발생하며, 만약 Transaction이 없다면 새로운 Transaction을 만드는 비용이 발생이 하게  된다.

 

반면 saveAll()의 경우는 Bean 객체 내부 함수를 호출하기 때문에 save()를 매번 호출하더라도 Transaction을 새로 생성하거나 참여되는 과정이 발생되지 않아 saveAll() 성능이 더 좋은 것이다.

 

결론


즉, 저장할 대상의 데이터가 많다면 save()보다는 saveAll()을 통해 로직을 구현하는 것이 성능상 좋다.

 

 

댓글