[DB] PK는 자연키? 대체키? 복합키?

시작 하기 전

DB 는 크게 NoSQLRDBMS, 2가지로 나뉘어진다.

RDBMS는 관계형 데이터베이스 관리 시스템을 의미하고, NoSQL은 RDB 형태의 관계형 데이터베이스가 아닌 다른 형태의 데이터 저장 기술을 의미한다.

RDBMS 를 사용해봤다면, Primary Key, Foreign Key 등은 들어 봤을테고, PK, FK 를 이용해 DB 스키마의 관계를 지정 할 수 있고 보통은 간단하고 익숙한 AUTO INCREMENT 를 많이 사용했을거라고 생각한다.

이렇다 보니 본인도 그냥 습관적으로 AUTO INCREMENT 를 사용했고, 이 값은 unique 하고 nullable 하지 않으니 가장 좋은 방법일거라고 생각했다. 하지만 좀 더 공부를 하다보니 차이점을 다른 방법이 존재하고, 이 내용을 기록하기 위해 포스트를 작성한다.


key 종류

Database 에서 row(=데이터, 레코드) 를 구분 할 수 있도록 Key(=키) 를 사용하고, 그 종류는 아래와 같다.

  • Composite Key: 두 가지 이상의 컬럼으로 이루어진 키.
  • Natural Key: 현실 세계의 속성으로 적용된 키. 이메일, 주민등록번호 등.
  • Surrogate Key: 비즈니스적 의미가 없는 키. 정수형 식별자 등.
  • Candidate Key: Primary Key가 될 수 있는 unique 한 키의 모음. Composite Key도 해당될 수 있음.
  • Primary Key: 해당 엔티티의 식별자 키.
  • Alternate Key: Primary Key 대신 사용할 수 있는 레코드의 unique 키.
  • Foreign Key: 외부 엔티티의 PK를 가리키는 키.

Natural Key, Surrogate Key는 간단히 말해서 비즈니스적인 의미, 애플리케이션에서 활용할 여지가 있는 unique한 데이터로 이루어진 컬럼인지 차이다.

언급했듯이 이메일, 주민등록번호 같은 데이터는 어떤 의미(전자메일 주소, 개인 식별 번호)가 있는 데이터기 때문에 Natural Key에 해당하지만 1, 2, 3, … 으로 증가하는 AUTO INCREMENT 정수형 식별자는 어떤 의미도 없기 때문에 Surrogate Key에 해당하는 것이다.


뭘 사용해야 하지?

데이터는 누적이다. 시간이 지날수록 쌓이고, 그만큼 무거워(?)진다.


이메일(Natural Key)도 unique not null 하고, 정수식별자(Surrogate Key)도 unique not null 하다면 불필요한 정수식별자 데이터를 쌓아갈 필요 없이 Surrogate Key 대신 Natural Key 를 사용하는게 더 좋은 방법일까?

물론 클라이언트의 요구사항에 따라 다르겠지만, 사용자의 식별자, 즉 개인정보에 해당 할 수 있는 이메일이나 아이다 같은 부분을 노출하고 싶지 않은 경우에는 Surrogate Key 를 사용하는게 좋은 방안이 될 수 있다.

반대로 클아이언트가 이런 정보를 노출하기를 원한다면, 굳이 Surrogate Key 에 대한 데이터를 누적시킬 필요 없이 이메일 같은 Natural Key 를 사용하는게 좋을 수 있다.

중요한 차이는 이메일 주소는 Database 레코드 상으로는 unique 하며 not null 이지만, 현실 세계에서는 소실 될 수 있는, stable 하지 않는 key 라는 것 이다. 없을거라고 생각하지만, 만에하나 이메일 서비스가 종료 된다면 해당 이메일 주소는 더 이상 유효하지 않기 때문에 추후 사용자가 비밀번호 재설정 등 이메일 주소를 사용하게 될 경우 문제가 발생 할 수 있다.

즉 레코드를 구별하즌 식별자가 현실 세계의 영향을 받을 수 있다는 점은 이메일 컬럼을 Primary Key 로 사용하기 전 충분히 고려해봐야 한다는 의미이다. 물론 정수형 식별자도 분산 데이터베이스 환경에서 문제가 될 수 있다.


unique 컬럼의 인덱스

MySQL 에서는 unique 한 컬럼에 대하여 UNIQUE INDEX 를 생성한다. 보통 JPA 에서는 아래 처럼 코드 엔티티를 디자인 하게 되는데 그럴 경우 아래 쿼리가 실행된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity(name="admin") 
public class AdminEntity extends BaseTimeEntity {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "id", nullable = false, updatable = false, insertable = false)
	private Long id;
	
  @Column(name = "email", unique = true, nullable = false)
  private String email;

	@Column(name = "nickname", nullable = false)
	private String nickname;
1
2
create table admin (id bigint not null, email varchar(255) not null, name varchar(255) not null, primary key (id)) engine=InnoDB
alter table admin add constraint UK_gig8560dcl8rp20ak28x7pjki unique (email)

엔티티의 email 필드에 @Column 어노테이션의 unique 속성을 설정했기 때문에 테이블을 생성한 후 unique constraint를 거는 것을 볼 수 있다. MySQL Workbench에서 확인해보면 역시 인덱스가 생성된 것을 볼 수 있다.

그렇기 때문에 약 10000 건의 레코드가 저장된 데이터베이스에서 식별자 컬럼을 기반으로 조회하든 이메일 컬럼을 기반으로 조회하든 같은 실행 계획을 가지는 것을 볼 수 있다


저장 공간의 차이?

만약 데이터 저장 공간에 민감한 환경이라면 Surrogate Key(정수 식별자) 를 제거하고 Natural Key(이메일) 을 Primary Key 로 사용해서 불필요한 인덱스를 없애는 것도 가능하겠지만, 그럴 경우가 그렇게 많지는 않을거라고 생각한다.

위에서 생성한 테이블에 10,000의 데이터를 집어 넣고 정수식별자와 이메일컬럼을 둘 다 사용하는 경우와 이메일 컬럼만을 사용하는 경우를 비교하면 데이터 저장공간의 1/3 정도를 차지하는 인덱스 저장 공간이 사라졌기 때문에, 용량이 확실히 줄어든 걸 확인 할 수 있다.

키 자체의 크기 차이(정수, 문자열 자로형)도 Stackoverflow 의 답변으로는 전체 성능에 다이나믹한 영향을 주지 않는다고 한다.


결론

솔직히 정답은 잘 모르겠다. unique 인덱스(이메일)를 사용해서 저장공간을 줄이느냐, 아니면 정수식별자(Auto Increment)를 사용해 미세하지만 조금이라도 빠른 성능을 기대하냐.

나라면, 일단 unique 인덱스로 개발을 진행하고 나중에 이메일 서비스가 전세계적으로 종료 된다면 그떄 정수식별자를 추가하고 복합키를 이용하는게 더 나은 방향이 아닌가 생각이 들기 때문에 이렇게 진행하지 않을까?

의견이 있다면 댓글로 남겨주시면 감사합니다.

Tags:

Updated:

Leave a comment