[JPA] 기본키(PK) 자동 생성 전략

Mineru·2022년 8월 27일
0

기본 키 생성 전략 공부 동기

실무 프로젝트를 하면서 기존에 Node 계열에서는 거의 안보이던 기본 키 생성 전략이라는 개념이 보이게 되면서 혼란스러워서 따로 정리를 해서 개념을 익혀둘 필요성을 느꼈다.

현재 실무 프로젝트에서는 기본키 자동 생성 전략으로 IDENTITY를 사용하고 있다.

별도로 Spring을 공부하지 않고 처음으로 백엔드로 Spring을 사용하면서 굉장히 낯설었지만, 일이 주어진 이상 할 수 밖에 없기에 차근차근 하나씩 진행을 해보았다.

하지만 IDENTITY를 사용하지 않으면 Entity 같의 관계를 맺는 경우에 에러가 발생하는 것을 보면서 이상함을 느꼈다.
IDENTITY 전략을 사용하면 기본적으로 1번 Table에서 id가 1인 row를 생성하게 되면 다음 2번 Table에서는 id가 2인 row가 생성이 된다.

이게 시간이 지나면 지날수록 row가 많아질탠데 이러한 상태가 되는 점이 조금 이상하긴 하지만 제일 중요한 관계가 맺어지지 않기에 어쩔 수 없지 진행을 했다.

https://devjem.tistory.com/40 이 블로그로 부터 원인을 알아냈다.
hibernate 설정 값으로 use-new-id-generateor-mappings: false을 넣어서 사용하면 해당 문제가 해결 된다고 한다.

기본 키 자동 생성 전략 4가지

AUTO

기본 키 속성 유형에 따라 값(IDENTITY, TABLE, SEQUNCE 중에서)을 결정한다.

@Id
// @GeneratedValue(strategy = GenerationType.AUTO) or
@GeneratedValue
private Long id;
@Id
// @GeneratedValue(strategy = GenerationType.AUTO) or
@GeneratedValue
private UUID id;

IDENTITY

기본 키 생성을 데이터베이스에 위임하는 전략이다.
흔히 DDL로 기본 키를 AUTO INCREAMENT 명령어로 해당 컬럼을 정의하는 방식이다.

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

특징

해당 전략은 Entity를 생성 후 save() 메소드를 통해 INSERT 후에 식별자를 조회하여 반환해서 해당 결과를 받아 객체에 담아낸다.

즉, INSERT 쿼리를 날린 뒤에 id가 생성 된다고 이해하면 된다.

  • id를 DB에 삽입 되고 난 뒤에 알게 되었을 때 문제점
    - 영속성 컨텍스트에서 해당 객체가 관리되려면 id 값이 있어야 한다.
    • 따라서 INSERT 쿼리를 날린 후에 추가적인 작업이 필요하다면 save()를 하고 난 뒤에 반환 된 값을 가지고 이후에 로직으로 동작을 해야 한다.

단점

어떠한 값이 나올지는 DB가 키를 생성하다보니 INSERT 된 이후에 id을 알 수 있게 되어서 다른 Entity와 관계를 동시에 맺은 채로 배치 INSERT가 불가능하다.

SEQUENCE

DB Sequence Object를 사용한다.
DB Sequence는 유일한 값을 순서대로 생성하는 특별한 DB Object.
테이블마다 Sequence Object를 따로 관리하고 싶으면 @SequenceGeneratorsequenceName 속성을 추가한다.

예를 들면, A Table, B Table, C Table 이렇게 세 개의 테이블이 존재할 때 A Table, C Table은 절대 id 값이 중복 되면 안되고 B Table은 중복이 되어도 상관이 없는 경우라고 가정하면
A TableC Table은 동일한 sequenceName으로 설정하고 B Table은 별도의 sequenceName을 설정해 사용하면 된다.

@SequenceGenerator(
  name = "TEST_ENTITY_SEQ_GENERATOR", // 필수값
  sequenceName = "TEST_ENTITY_SEQ", // 기본값 : hibernate_sequence
  initialValue = 1, // 기본값 : 1
  allocationSize = 1 // 기본값 : 50)
public class TestEntity {
	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE,
					generator = "TEST_ENTITY_SEQ_GENERATOR")
	private Long id; 
}

지원하는 데이터베이스

  • mariadb -> 10.3 이상 부터 지원
  • Oracle
  • H2
  • DB2

지원하지 않는 데이터베이스

  • MySQL

특징

IDENTITY 전략과 동일하게 DB에 INSERT 되고 나서 id 값을 알 수 있다.
하지만 IDENTITY 전략과 다른 이유는 sequenceName으로 부터 현재 id 값을 받아와서 다음 값을 미리 알 수 있다는 점이다.(이게 어떻게 동작하는지 직접 코드를 작성하면서 알아봐야 할 것 같다.)

SEQUENCE 전략은 Sequence를 매번 DB에서 네트워크를 통해 가져오기 때문에 성능 저하가 일어 날 수 있다.
따라서 @SequenceGenerator 어노테이션에서는 추가적으로 allocationSize라는 속성 값이 있다.
이는 기본값이 50이다.
allocationSize는 이론적으로는 더 큰 수로 설정할수록 성능이 좋아진다고 하지만, 서버를 중단하는 시점에 사용하지 않은 sequence 값이 날라가기 때문에 이때 DB 전달 되지 않은 값들은 날라가는 현상이 생기게 된다.
따라서 50~100 사이로 설정하는 것을 권장한다고 한다.

DB에 해당 서비스 로직이 네트워크 비용을 얼마나 많이 소모하고 있는지 모니터링을 하고 이를 개선해야할 타이밍에 allocationSize를 조절하는 것이 좋을 것 같다.
빠른 네트워크 회선을 사용하는 경우 비용이 증가가 되는 DB 서비스들이 있다보니 이러한 경우라면 서비스 유지 비용 절감에 매우 좋을 것 같다.

TABLE

키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략

@SequenceGenerator(
  name = "TEST_ENTITY_SEQ_GENERATOR",
  table = "SEQUENCE_MANAGER",
  pkColumnValue = "",
  allocationSize = 1 // 기본값 : 50)
public class TestEntity {
	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE,
					generator = "TEST_ENTITY_SEQ_GENERATOR")
	private Long id; 
}

특징

모든 데이터베이스에서 해당 기능을 지원한다.

Table 전략에서 allocationSize를 1을 초과하는 값을 설정하게 되면 웹 서버를 여러개 띄울 경우에는 sequence가 맞지 않는 문제가 발생할 수 있지 않을까라고 할 수 있지만, 이는 서버 별로 순차적으로 사용하여 값이 올라가 있을거라고 한다.(직접 사용해보지 않았기 때문에 어떻게 동작할지 예상이 되지 않는다.)

단점

  • 최적화 되어있지 않은 테이블을 직접 사용하기 때문에 성능상의 이슈가 있다.
  • 운영 서버에서는 사용하기에 적합하지 않다.

기본 키 전략별 성능 테스트

출처 : https://github.com/HomoEfficio/dev-tips/blob/master/JPA-GenerationType-%EB%B3%84-INSERT-%EC%84%B1%EB%8A%A5-%EB%B9%84%EA%B5%90.md

출처 참고

profile
Daily Coding

0개의 댓글