Search
Duplicate
📒

[Database Study] 01-x. SNS 모델링으로 배우는 정규화 / 비정규화

상태
미진행
수업
Database Study
주제
SQL
4 more properties
참고

정규화 / 비정규화

NOTE
정규화 데이터베이스중복을 최소화하는 데이터베이스 비정규화 데이터베이읽기 시간을 최적화하는 데이터베이스
정규화는 중복을 줄이고, 비정규화는 join을 줄여줌..

정규화

NOTE
관계형 데이터베이스에서 중복을 최소화하기 위해 데이터를 구조화 하는 작업을 말한다!
보통 3 정규형까지 쓴다.
차수가 높아질수록 만족시켜야할 제약 조건이 늘어난다.
장점
데이터베이스 변경 시 이상 현상 제거
저장 공간의 최소화 기능
효과적인 검색 알고리즘 생성 가능
데이터 삽입 시 관계 재구성 필요성 감소
데이터 구조의 안전성 및 무결성 유지
단점
테이블간 JOIN 연산 증가 → 쿼리 시간이 길어짐

비정규화

NOTE
하나 이상의 테이블에 데이터를 중복해 배치하는 최적화 기법!
의도적으로 정규화를 위배하는 행위이다.
비정규화 대상
자주 사용되는 테이블에 엑세스하는 프로세스의 수가 가장 많고, 항상 일정한 범위만을 조회
테이블에 대량 데이터가 있고, 자주 처리하는 경우
지나치게 조인이 많이해서 조회하는 것이 기술적으로 어려운 경우
장점
빠른 데이터 조회 → 조인 비용이 줄어들기 때문
살펴볼 테이블이 줄어들기 때문에 쿼리가 단순해짐
단점
데이터 갱신이나 삽입 비용이 높음
데이터의 일관성이 깨질 수 있음
중복 저장하기에 더 많은 저장공간이 필요해짐

실습 (SNS 모델링)

NOTE
실제 SNS에서 사용되는 DB형식을 가지고 정규화/비정규화를 작성한다!
사용 API 목록
# 사용자 테이블 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) );
SQL
복사
실제 사용한 테이블 내용들
JDBC를 주로 사용하는데 이에 대한 설명은 생략한다.

EasyRandom

record

Usecase 사용

NOTE
Domain - Domain간 통신을 위해 Usecase작성에서 흥미로운 주제가 나와서 기록한다!
@RequiredArgsConstructor @Service public class CreateFollowMemberUsecase { final private MemberReadService memberReadService; final private FollowWriteService followWriteService; public void execute(Long fromMemberId, Long toMemberId){ /* 1. 입력받은 memberId로 회원조회 2. FollowWriteService.create() */ var fromMember = memberReadService.getMember(fromMemberId); var toMember = memberReadService.getMember(toMemberId); followWriteService.create(fromMember, toMember); } }
Java
복사
ex) Member id값 2개로 Follow 생성
@RequiredArgsConstructor @Service public class GetFollowingMembersUsecase { final private MemberReadService memberReadService; final private FollowReadService followReadService; public List<MemberDto> execute(Long memberId) { var followings = followReadService.getFollowings(memberId); var followingMemberIds = followings.stream().map(Follow::getToMemberId).toList(); return memberReadService.getMembers(followingMemberIds); } }
Java
복사
ex) Member에서 Member의 Follow 목록 MemberList 가져오기
\

실습 과정

NOTE

히스토리성 데이터는 정규화 대상이 아니다.

@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;; } }
Java
복사
Member의 nickname이 중복으로 포함된다.
요구사항에 따라 데이터의 최신성을 보장하지 않아도되는 경우 정규화 대상이 아니다.
사용자의 닉네임 변경기록은 최신데이터가 아닌 과거의 내역을 보관하므로 정규화를 하지 않는다.

정규화 하는 경우

@Getter public class Follow { final private Long id; final private Long fromMemberId; final private Long toMemberId; final private 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; } }
Java
복사
중복되는 데이터가 없도록 설계
팔로우의 경우 데이터의 최신성을 보장해야 한다.
만약 수백만명의 팔로우가 있는 인플루언서가 무언가를 수정하는데 정규화가 되어있지 않으면 팔로우된 수백만명의 데이터를 모두 수정해야한다!

실습내용 정리

NOTE

중복된 데이터면 반드시 정규화를 해야하나?

중복 데이터면 무조건 정규화를 하는건 고려해봐야할 일이다.
정규화도 비용이다. (읽기 비용 - 쓰기 비용의 트레이드 오프)

정규화시 고려해야할 점

데이터의 최신성을 얼마나 보장해야하는가?
히스토리성 데이터는 정규화 대상이 아니다.
데이터의 변경 주기와 조회 주기
자주 변경되는 데이터일수록 쓰기에 이점을 가져가도록 한다.
객체(테이블) 탐색 깊이
A→B→C→D의 깊이가 B→D를 가지게할수 있음 (읽기 향상) 하지만 C의 D참조가 변경되면 B도 수정해야함 (쓰기 저하)

정규화를 한다 → 읽기시 데이터를 어떻게 가져올것인가?

조인을 하는건 언제나 최후의 보루이다.
조회시 성능이 좋은 DB, 캐싱, 최적화 기법등을 먼저 고민해보자
조인을 하는건 무엇보다 결합도가 너무 높아진다.
읽기 쿼리를 한번 더 실행하는건 생각보다 큰 비용이 아닐 수 있다.