PK 설계와 인덱싱 과정

아이스__아메리·2023년 10월 17일
0

DB

목록 보기
4/4

기본 개념은 각설하고 인덱싱을 어떻게 처리하는 과정을 담았습니다.

인덱싱이 필요하다

적용전 엔티티

@Table(indexes = {
        @Index(name = "appId__idx", columnList = "appId", unique = true)
})
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class App extends TimeBaseEntity {
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "app_abc_id", columnDefinition = "BIGINT(20) UNSIGNED")
    private Long id;
    private String appId; //비즈니스로직에서 UUID를 생성해서 저장
}

데이터베이스를 설계할때 애플리케이션 내부용 키로는 increment pk를, 외부에 공개할 키로는 uuid를 사용하는 것을 권장한다.
위의 코드는 appId가 노출 되는 경우 재발급까지 고려해서 id는 자동생성되는 PK와 인증이후 발급받는 appId를 구분하자는 취지에서 두 개 모두 선언했다. 하지만 API를 만들려고보니 혼동의 여지와 조회성능을 이슈가 발생할거같아 그래서 여러가지 해결방법을 생각해보았다.

Multi PK로 변경

id, appId 모두 Auto Increment 와 UUID 두 개 모두 사용하여 PK를 지정한다.
-> appId 는 고유한 ID이기때문에 변동가능성이 거의 없다고해도 무방하다. 그래서 테이블 설계 후 굳이 인덱스를 설정하지말고 PK로 설정한다.
+ Multi PK로 인한 성능에 큰 차이가 없다.
- 16바이트 문자열 길이로 인한 조회 이슈가 발생한다.
그래서 전혀 개선되지 않기 때문에 시도조차 하지않았다.

단일 PK, UUID로 변경

일단 숨겨지는 ID와 노출되는 ID의 통합이다.

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class AppToken extends TimeBaseEntity {
    @Id
    @Column(name = "app_id")
    private String id
}

PK는 기본적으로 인덱싱이 되어있기때문에 엔티티와 테이블을 파악하는데에 있어서 훨씬 단순해 졌다.

+ 혼동의 여지를 없앤다.
+ 분산 시스템에서 PK 충돌을 방지한다.
- 여전히 16바이트 문자열 길이로 인한 인덱스 크기가 크고 데이터베이스에 더 많은 저장 공간을 차지 한다.

숫자형 PK로 변경

조회 이슈에 대한 해결방안이다. 문자형으로 UUID를 만들었을때 가장 큰 문제는 인덱싱을해도 뒤죽박죽한 문자열로 인한 조회성능이 좋지 않다.
+ 숫자형 PK는 비교적 짧은 길이를 가지기 때문에 인덱스 크기가 작고 저장공간을 적게 차지한다.
- 숫자형은 문자형에 비해 경우의 수가 작기때문에 다중 데이터베이스나 분산 시스템에서 PK 충돌 발생가능성이 있다.

마무리

당시에는 문자열길이가 2048이여서 인덱스 크기가 너무 커져서 적용이 안되는 이슈가 발생했는데 만약 그때 이슈가 발생하지 않았다면 조회성능 이슈가 추후에 알게되었을거고 시간비용이 많이 들었을거같아 다행이라고 생각합니다. 재현이 안됨

생성 당시에 NotNull일 경우 PK로 설정하고 닉네임과 같이 생성된 후 설정하는 경우 인덱싱을 거는 방법으로 기조를 세웠다.

현재의 테이블에 성격에 맞춰 테이블을 변형 시켰지만 성능 차이는 주로 인덱스의 크기와 데이터베이스의 크기에 따라 달라지므로, 실제 운영 환경에서 성능 테스트를 진행하여 두 가지 방식을 비교하는 것이 가장 확실한 방법이다. 또한 데이터베이스의 설정, 인덱싱 방법, 쿼리 튜닝 등도 고려하여 종합적인 성능을 평가하는 것이 중요하다.

profile
츠케멘 좋아

0개의 댓글