간단한 토이프로젝트로 SNS모델링을 진행하며 학습한 내용입니다.
관계형 데이터베이스에서 중복을 최소화하기 위해 데이터를 구조화 하는것
보통 3차 정규형까지 사용하며 차수가 높아질수록 만족시켜야할 제약이 늘어남
장점
중복을 제거하고 한 곳에서 관리
저장공간 최소화
데이터 정합성 유지하기가 유리함
데이터 삽입 시 관계 재구성 필요성 감소
단점
하나 이상의 테이블에 데이터를 중복으로 배치하는 최적화 기법
비정규화 대상
자주 사용되는 테이블에 액세스하는 프로세스의 수가 가장 많고,
항상 일정한 범위만을 조회하는 경우
테이블에 대량 데이터가 있고 자주 처리하는 경우
지나치게 조인을 많이 사용하게 되어 조회하는 것이 기술적으로 어려울 경우
장점
빠른 데이터 조회
(Join 비용이 줄어들기 때문)
살펴볼 테이블이 줄어들기 때문에 데이터 조회 쿼리가 간단해짐
단점
데이터 갱신이나 삽입 비용이 큼
데이터 간의 일관성이 깨어질 수 있음
데이터를 중복하여 저장하므로 더 많은 저장 공간이 필요
Note :
정규화 데이터베이스 : 중복을 최소화하는 데이터베이스
비정규화 데이터베이스 : 읽기 시간을 최적화하는 데이터베이스
요구사항
create table Member
(
id int auto_increment,
email varchar(20) not null,
nickname varchar(20) not null,
birthday date not null,
createdAt datetime not null,
constraint member_id_uindex
primary key (id)
);
create table MemberNicknameHistory
(
id int auto_increment,
memberId int not null,
nickname varchar(20) not null,
createdAt datetime not null,
constraint memberNicknameHistory_id_uindex
primary key (id)
);
create table Follow
(
id int auto_increment,
fromMemberId int not null,
toMemberId int not null,
createdAt datetime not null,
constraint Follow_id_uindex
primary key (id)
);
Jdbc로 JPA를 구현했었는데 이는 생략한다.
@Getter
public class Member {
final private Long id;
private String nickname;
final private String email;
final private LocalDate birthday;
final private LocalDateTime createdAt;
final private static Long NAME_MAX_LENGTH = 10L;
@Builder
public Member(Long id, String nickname, String email, LocalDate birthday,
LocalDateTime createdAt) {
this.id = id;
this.email = Objects.requireNonNull(email);
this.birthday = Objects.requireNonNull(birthday);
validateNickName(nickname);
this.nickname = Objects.requireNonNull(nickname);
this.createdAt = createdAt == null ? LocalDateTime.now() : createdAt;
}
}
@Getter
public class MemberNicknameHistory {
final private Long id;
final private Long memberId;
final private String nickname;
final private LocalDateTime createdAt;
@Builder
public MemberNicknameHistory(Long id, Long memberId, String nickname, LocalDateTime createdAt) {
this.id = id;
this.memberId = memberId;
this.nickname = nickname;
this.createdAt = createdAt == null ? LocalDateTime.now() : createdAt;;
}
}
두 Entity에는 같은 nickname 필드가 정의돼있지만 정규화 하지 않는다.
@Getter
public class Follow {
private final Long id;
private final Long fromMemberId;
private final Long toMemberId;
private final LocalDateTime createdAt;
@Builder
public Follow(Long id, Long fromMemberId, Long toMemberId, LocalDateTime createdAt) {
this.id = id;
this.fromMemberId = Objects.requireNonNull(fromMemberId);
this.toMemberId = Objects.requireNonNull(toMemberId);
this.createdAt = createdAt == null ? LocalDateTime.now() : createdAt;
}
}
팔로우같은 경우 정규화 해야한다.
수 백만 팔로워를 가진 인플루언서가 무언가를 수정했을 때 데이터가 정규화 돼있지 않다면?
모든 팔로우의 데이터를 수정해야한다.
팔로우의 경우 데이터의 최신성을 보장해야 한다.
중복된 데이터를 무조건 정규화 하는것은 아니다.
중복된 데이터를 무조건 정규화 하는것은 고려해봐야할 사항이다.
정규화도 비용이다 (읽기 - 쓰기 비용간의 트레이드 오프)
정규화의 고려해야할 점
데이터의 최신성을 보장해야 하는가?
히스토리성 데이터는 정규화 대상이 아니다.
데이터 변경 주기와 조회 주기
객체 탐색 깊이가 얼마나 깊은가