목표

  • MySQL 을 사용 중인 환경에서 Hibernate의 각 GenerationType이 PK를 생성하는 방식을 이해한다.

GenerationType

  • GenerationType: JPA에서 PK를 생성하는 전략을 나타내는 enum
    • TABLE: 데이터베이스 테이블을 사용해서 PK 할당
    • SEQUENCE: 데이터베이스 시퀀스를 사용해서 PK 할당
    • IDENTITY: 데이터베이스의 식별자 컬럼(MySQL의 AUTO_INCREMENT)을 사용해서 PK 할당
    • UUID: 애플리케이션에서 UUID를 생성해서 PK 할당
      • JPA 3.1, Hibernate 6.2부터 지원 시작
    • AUTO: 사용 중인 데이터베이스 종류에 따라서 자동으로 전략 선택

GenerationType.AUTO의 전략 선택 과정

Hibernate 5.2

  • Spring Boot 2부터 hibernate.id.new_generator_mappingstrue로 바뀌면서, MySQL 을 사용할 때 AUTO를 사용하면 UUID 타입의 경우를 제외하고는 TABLE 전략을 사용해서 PK를 생성하게 된다.

Hibernate 6.0부터

  • 6.0 부터는 동작이 단순화되었다.
  • 아래와 같이 동작하면서 MySQL에서 AUTO 방식을 사용할 때는 IDENTITY로 동작하는 경우가 절대 없어졌다.

IDENTITY 전략의 특징

  • IDENTITY 방식은 INSERT 쿼리를 먼저 실행해야 ID를 얻을 수 있기 때문에 쓰기 지연을 할 수가 없다.
  • 또한 IDENTITY 방식은 batch insert를 할 수 없어 대량의 쓰기가 자주 발생한다면 성능에 영향을 줄 수 있다.

TABLE 전략의 특징

  • allocationSize 만큼 테이블에서 id를 가져와서 메모리에 가져와서 DB 왕복을 줄인다.
  • id를 가져오고 업데이트하기 위한 쿼리는 원래 저장하려는 엔티티와 별도 트랜잭션으로 실행되어 동시성 성능이 떨어지는 것을 방지한다.
  • 예를 들어 아래와 같은 Product 엔티티를 저장할 때 현재 메모리에 id 정보가 없다면 3개의 쿼리가 발생할 것이다.

IDENTITY vs TABLE

실험해보기

  • hibernate 6.4.1을 기준으로 여러 개의 엔티티를 추가할 때의 성능을 비교해본다.
  • Player: TABLE 전략 사용. allocationSize=1000
  • Player2: IDENTITY 전략 사용.
  • 아래와 같이 Spring 통합 테스트로 saveAll() 메서드를 사용해서 batch-insert 를 시도했을 때 성능을 비교해본다.
    • hibernate.jdbc.batch_size:1000 으로 batch size를 설정했다.
    • 아래 표와 같이 엔티티의 수가 적을 때는 id 채번을 위해 추가 쿼리가 발생하여 TABLE 방식이 느리지만, 엔티티 수가 많을 수록 기하급수적으로 IDENTITY 방식의 처리 시간이 느려지는 것을 확인할 수 있다.
n(엔티티 수)TABLE(ms)IDENTITY(ms)증가 비율
1151920.61
2225940.42
102201900.86
502339053.88
250296364012.3
500364696319.13
10004281341731.35
500011796051351.33

결론

  • 적은 개수를 insert 할 때는 절대적은 ms 차이가 크게 나는것은 아니지만, 개수가 늘어날 수록 IDENTITY는 기하급수적으로 느려지고 있었다.
  • 직접 테스트해본 결과를 믿고 TABLE 방식을 사용하고자 한다.

참고 자료