데이터베이스

[Database] 자연키(Natural key)와 대체키(Surrogate Key), PK(기본키)를 대체키로 설정해야 하는 이유

망나니개발자 2023. 2. 14. 10:00
반응형

현재 사내 데이터베이스의 PK가 모두 자연키로 설정되어 있습니다. 데이터베이스 구조를 개선하면서 해당 부분을 대체키로 개선하려고 하는데, 이번에는 PK를 대체키로 설정해야 하는 이유에 대해 살펴보도록 하겠습니다. 아래의 내용은 RealMySQL, 자바 ORM 표준 JPA 프로그래밍, 실무 경험 등을 바탕으로 작성한 내용입니다.

 

 

 

 

1. PK(기본키)를 대체키로 설정해야 하는 이유


  • 변경 가능성을 제거할 수 있음
  • 성능을 향상시킬 수 있음
  • 테이블 구조와 관련 코드를 간결하게 만들 수 있음
  • 데이터의 무결성을 보장할 수 있음

 

 

[ 변경 가능성을 제거할 수 있음 ]

과거에 주민번호를 PK로 사용하는 것이 일반적이던 시절이 있었다. 주민번호는 모든 사람에게 반드시 부여되며(NOT NULL), 유니크하고(UNIQUE), 절대 변하지 않을 것이므로 PK로 삼기에 적합해보였다.

그런데 문제는 정부 정책이 변경되면서, 법적으로 PK를 저장할 수 없게 된 것이다. 결국 PK와 FK 등을 주민번호로 사용하던 모든 데이터베이스 구성을 변경해주어야 했고, 이러한 변경은 비즈니스 로직에도 영향을 주게 되었다.

PK를 변경하는 것은 단순히 번거로움 뿐만 아니라 데이터베이스의 성능에도 악영향을 줄 수 있다. 특히 MySQL과 같이 PK가 레코드의 저장 위치가 결정하는 키라면 성능에 엄청난 악영향을 주게 된다. MySQL에서는 PK가 레코드의 물리적인 저장 위치를 결정하기 때문에, 인덱스는 PK에 의존한다. 그래야 인덱스를 타고 들어와서 PK를 통해 저장된 위치에서 레코드를 읽어올 수 있기 때문이다.

 

 

PK가 변경된다는 것은 레코드의 물리적인 저장 위치가 바뀌어야 함을 의미하며, 이는 인덱스까지 재구성하도록 영향을 준다. 이는 엄청난 디스크 작업을 필요로 하는데, 관련된 자세한 내용은 PK 포스팅에서 살펴보도록 하자.

비즈니스 영역에서 절대적으로 변하지 않는 것은 없으며, 테이블은 한번 정의하면 변경이 매우 어렵다. 그렇기 때문에 특히나 요즘처럼 비즈니스가 급변하는 시대에서 핵심 영역이 아닌 데이터베이스를 비즈니스 영역과 결합시키는 것은 좋지 않다. 그러므로 PK를 대체키로 선언하여 발생할 수 있는 모든 변경 가능성을 제거하는 것이 좋다.

 

 

 

 

[ 성능을 향상시킬 수 있음 ]

PK를 대체키로 사용하게 되면 일반적으로 자동 증가 숫자를 사용하게 되므로 성능을 향상시킬 수 있다. 반대로 자연키를 PK로 사용하는 경우 일반적으로 문자열로 사용되며, 심지어 복합키로 구성되는 경우가 많은데, 이는 PK를 비대하게 만들어 성능에 좋지 못하다.

일반적으로 문자열을 사용하는 것은 숫자를 사용하는 것보다 느리다. 예를 들어 WHERE 절에 조건을 거는 것은 PK로 데이터를 필터링하는 작업인데, 일반적으로 문자열 비교는 숫자 비교가 훨씬 느리며 이는 데이터가 크고 많을수록 안좋아진다.

또한 비즈니스와 결합된 PK는 자동 증가 값을 PK로 사용했을 때보다 훨씬 큰 값을 갖는 경우가 많다. 주민번호와 같은 문자열은 단순 숫자보다 훨씬 크며, 심지어 복합키로 구성된다면 PK는 훨씬 비대해진다. 그리고 이는 전반적인 데이터베이스 구성(PK 테이블, 인덱스 등)에 좋지 않다. 자세한 내용은 PK 포스팅에서 페이지 부분을 참고하도록 하자.

사실 성능은 단편적으로 따지기 어렵다. 왜냐하면 MySQL과 같이 PK를 테이블의 클러스터링 키로 사용(PK를 기준으로 유사한 데이터를 모아서 저장)하는 경우라면 PK로 조회 시에 성능을 높이는 효과도 있기 때문이다. 하지만 그럼에도 불구하고 일반적으로 PK의 크기는 작을수록 좋으며, 원시 타입일수록 좋다.

아래의 내용은 RealMySQL에서 5개의 인덱스를 갖는 테이블의 PK 크기에 따라 인덱스의 크기를 비교한 표이다. 레코드 한 건을 생각하면 50바이트 쯤이야 대수롭지 않지만 레코드 건수가 100만건 만 돼도 인덱스의 크기가 190MB나 증가했다. 1000만건이 되면 1.9GB 증가한다. 또한 인덱스가 커질수록 같은 성능을 내기 위해 그 만큼의 메모리가 더 필요해지므로 PK는 신중히 선택해야 한다.

 

 

MySQL에서 PK를 Auto-Increment 값으로 쓰는 것과 UUID 값으로 쓰는 것의 퍼포먼스 차이를 비교한 포스팅도 있다. 해당 포스팅은 5.0 기반으로 작성되어 있긴 하지만, 그래도 확실히 성능 측면에서는 대체키를 사용하는 것이 좋음을 확인할 수 있다.

 



 

 

[ 테이블 구조와 관련 코드를 간결하게 만들 수 있음 ]

자연키를 PK로 사용할 때, 1개의 컬럼 만으로 PK를 구성하지 못하고 복합키로 설정해줘야 하는 경우도 상당히 많다. 문제는 복합키를 사용하게 되면서 테이블 구조와 관련 코드를 복잡하게 만들 수 있다는 점이다.

먼저 복합키를 PK로 갖는 테이블과 관계를 맺는 테이블이라면 해당 복합키를 다른 테이블도 가져야 한다. 예를 들어 (생년월일, 이름, 성별)을 PK로 갖는 사용자 테이블이 있다고 하자. 그러면 해당 키가 사용자를 식별할 수 있는 키이므로 무결성을 위해 사용자 주문 내역 테이블에도 (생년월일, 이름, 성별) 컬럼을 동일하게 가져야하는 것이다.

 

 

이는 전반적인 데이터베이스 구성을 복잡하게 만들 뿐만 아니라 문제를 유발하기 쉽다. 심지어 이를 활용하는 코드라면 항상 소스 코드 관점에서는 적어도 3개의 값이 파라미터로 함께 전달되어야 함을 의미한다.

하지만 대체키를 사용한다면 테이블과 데이터 모델을 보다 간결하게 만들 수 있다. 또한 소스 코드 수준에서도 전달해야 하는 값의 개수를 최소화시켜 불필요하게 DTO를 만들지 않아도 될 수도 있다.

 

 

 

 

[ 데이터의 무결성을 보장할 수 있음 ]

대체키를 사용한다는 것은 키의 생성 및 관리를 완전히 데이터베이스에 위임한다는 것이다. 반면에 자연키를 사용한다는 것은 유효성 검사와 데이터의 무결성 등을 개발자가 직접 관리한다는 것인데, 아무래도 사람이 직접 관리하는 것 보다는 시스템을 통해 이를 위임하는 것이 안정적이다. 그래야 일관된 방식으로 키를 생성 및 관리하여 데이터의 무결성을 보장할 수 있다.

 

 

 

 

 

 

 

JPA는 일관성있게 항상 대체키를 사용하는 것을 권장합니다. 그 외에도 PK가 주민번호와 같은 개인 정보라면 유출 위험 등의 부수적인 문제가 있어서 가급적이면 대체키를 사용하는 것이 요즘의 추세입니다.

그렇다고 하여 항상 대체키를 사용해야 하는 것은 아닙니다. RealMySQL 책에서도 자연키의 사용을 권장하고 있고, 실제로 성능적인 측면(MySQL PK의 클러스터링 특성 등)이나 데이터 읽기 용이성 등을 고려하면 자연키를 사용하는 것이 적합할 수 있습니다. 그러므로 반드시 대체키를 사용해야 한다기 보다는 경우에 따라서 자연키가 적합할 수도 있다는 것도 염두해두시면 좋을 것 같습니다.

 

 

 

 

 

반응형