ERD는 DB를 구축할 때 가장 기초적인 뼈대 역할을 하며, Relation 간의 관계들을 정의한 것.
서비스를 구축할 때 가장 먼저 신경 써야 할 부분.
시스템의 요구 사항을 기반으로 작성되며, ERD를 기반으로 DB가 구축됨.
DB 구축 이후에도 디버깅, 또는 비즈니스 프로세스 재설계가 필요한 경우에 설계도 역할을 담당.
ERD가 관계형 구조로 표현할 수 있는 데이터를 구성하는 데 유용할 수 있지만, 비정형 데이터를 충분히 표현할 수 없다는 단점이 있음.
-비정형 데이터
비구조화 데이터를 말하며, 미리 정의된 데이터 모델이 없거나 미리 정의된 방식으로 정리되지 않은 정보
요구 사항
-영업사원은 0 ~ n명의 고객을 관리
-고객은 0 ~ n개의 주문을 넣을 수 있음
-주문에는 1 ~ n개의 상품이 들어감

요구 사항
-선수들은 1명의 챔피언을 고를 수 있음
-챔피언은 1개 이상의 스킬을 가짐
-스킬은 1개 이상의 특성을 가짐

정규화 과정은 Relation 간의 잘못된 종속 관계로 인해 DB 이상 현상이 일어나서 이를 해결하거나, 저장 공간을 효율적으로 사용하기 위해 Relation을 여러 개로 분리하는 과정
DB 이상 현상이란, 회원이 한 개의 등급을 가져야 하는데 세 개의 등급을 갖거나, 삭제할 때 필요한 데이터가 같이 삭제되고, 데이터를 삽입해야 하는데 하나의 필드 값이 NULL 이 되면 안 되서 삽입하기 어려운 현상.
정규화 과정은 정규형 원칙을 기반으로 정규형을 만들어 가는 과정이며, 정규화 된 정도는 정규형(NF, Normal Form) 으로 표현.
기본 정규형인 제1 정규형, 제2 정규형, 제3 정규형, 보이스/코드 정규형이 있고,
고급 정규형인 제4 정규형, 제5 정규형이 있음.
같은 의미를 표현하는 Relation도 좀 더 좋은 구조로 만들어야 하고, 자료의 중복성은 감소해야 하고, 독립적인 관계는 별개의 Relation으로 표현해야 하며, 각각의 Relation은 독립적인 표현이 가능해야 하는 것.
Relation의 모든 도메인이 더 이상 분해될 수 없는 원자 값(atmoic value)만으로 구성되어야 하는 것.
Relation의 속성 값 중에서 한 개의 PK에 대해 두 개 이상의 값을 가지는 반복 집합이 있어선 안 됨.
반복 집합이 있다면 제거할 것.

첫 번째 도표를 보면 홍철, 범석은 각각 2개의 과목, 성취도를 개별 필드에 가지고 있다.
이를 분해해서 두 번째 도표처럼 각각의 ROW로 만들던지, 다른 테이블로 연결하던지 하라는 이야기로 보인다.
그렇게 되면 각 ROW의 모든 필드는 단 하나의 값을 갖게 되며, 이들은 더 이상 분해될 수 없는 atomic value라고 할 수 있을 것이다.
Relation이 제1 정규형이며, 부분 함수의 종속성을 제거한 형태를 말한다.
부분 함수의 종속성 제거란, PK가 아닌 모든 속성이 PK에 완전 함수 종속적인 것을 말한다.

위 도표를 보면, PK인 {유저ID, 수강명}과 완전 종속된 유저번호 Relation과 {유저ID, 수강명}에 따른 성취도 Relation으로 분리된 것을 볼 수 있다.
주의할 점은 Relation을 분해할 때, 동등한 Relation으로 분해해야 하고, 정보 손실이 발생하지 않는 무손실 분해가 되어야 한다는 것.
제2 정규형이고 PK가 아닌 모든 속성이 이행적 함수 종속(transitive FD)을 만족하지 않는 상태.
이행적 함수 종속이란, A -> B와 B -> C가 존재하면 논리적으로 A -> C가 성립하는데, 이 때 집합 C가 집합 A에 이행적으로 함수 종속이 되었다고 한다.


제3 정규형이고, 결정자가 후보키가 아닌 함수 종속 관계를 제거하여 Relation의 함수 종속 관계에서 모든 결정자가 후보키인 상태
-결정자
함수 종속 관계에서 특정 종속자(dependent)를 결정짓는 요소.
X->Y일 때, X는 결정자, Y는 종속자.
요구 사항
-각 수강명에 대해 한 학생은 오직 한 강사의 강의만 수강한다.
-각 강사는 한 수강명만 담당한다.
-한 수강명은 여러 강사가 담당할 수 있다.

{학번, 수강명} 또는 {학번, 강사}가 후보키가 되며, 만약 범석이라는 강사가 롤이라는 수강명을 담당한다고 했을 때, 이를 삽입하면 학번이 NULL이 되는 문제점 발생.
또한, 이 Relation은 다음과 같은 함수 종속 다이어그램을 가짐.

즉, 강사 속성이 결정자이지만 후보키가 아니므로 이 강사 속성을 분리해야 함.

보이스/코드 정규형을 만족한 Relation.
롤-범석이 제대로 들어갔으며 학번-강사/수강명-강사로 잘 분해된 모습을 볼 수 있음.
이렇게 정규형 과정을 거쳐 테이블을 나눈다고 해도 성능이 100% 좋아지는 것은 아님.
좋아질 수도, 나빠질 수도 있음
테이블을 나누게 되면 어떤 쿼리는 JOIN을 해야 하는 경우도 발생해서 오히려 느려질 수 있기 때문에 서비스에 따라 정규화/비정규화 과정을 진행.
--
DB에서 하나의 논리적 기능을 수행하기 위한 작업의 단위.
DB에 접근하는 방법은 쿼리이므로, 여러 개의 쿼리를 하나로 묶는 단위를 뜻함.
원자성은 트랜잭션과 관련된 일이 모두 수행 되었거나 모두 되지 않았거나를 보장하는 특징.
트랜잭션을 커밋했는데 문제가 발생하여 롤백하는 경우, 그 이후에 모두 수행되지 않음을 보장.
1,000만원을 가진 A가 0원을 가진 B에게 500만 원을 이체한다고 가정.
이 때 operation의 단위
1. A의 잔고 조회
2. A의 잔고 - 500만 원
3. B의 잔고 + 500만 원
1~3의 operation 중, DB 사용자는 이 3가지 과정을 보거나 난입할 수 없음.
이 과정 이후, [A의 잔고 + 500만 원, B의 잔고 - 500만 원]이 모두 완료된 상황만 볼 수 있음.
만약 중간에 이 작업을 취소하게 되면, 원래 상태 그대로 돌아감.
그래서 all or nothing인 것.
트랜잭션 단위로 여러 로직을 묶을 때, 외부 API를 호출하면 안 됨.
만약 있다면 롤백할 때 어떻게 할 것인지에 대한 해결 방법이 필요하고, 트랜잭션 전파를 신경 써서 관리해야 함.
커밋은 여러 쿼리가 모두 성공적으로 처리되었다고 확정하는 명령어.
트랜잭션 단위로 수행되며, 변경된 내용은 모두 영구적으로 저장.
커밋이 수행되었다는 것은 하나의 트랜잭션이 성공적으로 수행되었다는 의미.

update, insert, delete 쿼리가 하나의 트랜잭션 단위로 수행되고, 이후 DB에 영구 저장됨.

중간에 오류 등이 발생하여 다시 되돌려야 할 때, 롤백을 통해 지금까지 처리한 모든 과정이 일어나기 전으로 되돌림.
커밋, 롤백 덕분에 데이터의 무결성이 보장됨.
또한 데이터 변경 전에 변경 사항을 쉽게 확인할 수 있고, 해당 작업을 그룹화 할 수 있음.
트랜잭션을 수행할 때, 커넥션 단위로 수행하기 때문에 커넥션 객체를 넘겨서 수행해야 함.
이를 매 번 넘겨주기가 어렵고 귀찮음.
여러 트랜잭션 관련 메소드의 호출을 하나의 트랜잭션에 묶이도록 하는 것을 트랜잭션 전파라 함.
@Service
@Transactional(readOnly = true)
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
앞의 코드처럼 Spring 프레임워크에서는 @Transactional 어노테이션을 통해 여러 쿼리 관련 코드들을 하나의 트랜잭션으로 처리.
허용된 방식으로만 데이터를 변경할 수 있는 것을 의미.
DB에 기록된 모든 데이터는 여러 가지 조건, 규칙에 따라 유효성을 가져야 함.
예를 들어 잔고가 1,000만 원인 계좌에서 2,000만 원을 인출할 수 없음.
일반적인 잔고는 0 아래로 떨어질 수 없기 때문.
트랜잭션 수행 시, 서로 끼어들지 못하는 것을 의미.
복수의 병렬 트랜잭션은 서로 격리되어 마치 순차적으로 실행되는 것처럼 작동되어야 하고, DB는 여러 사용자가 같은 데이터에 접근할 수 있어야 함.
그냥 순차적으로 하면 쉽지만 성능은 나빠지기 때문.
격리성은 여러 개의 격리 수준으로 나뉘어 격리성을 보장.

격리 수준은 Serializable, Repeatable Read, Read Committed, Read Uncommitted 4가지 레벨이 존재하며, 위로 갈수록 동시성이 강해지지만 격리성은 약해지고, 아래로 갈수록 동시성은 약해지고 격리성은 강해짐.
Repetable Read는 Phantom Read, Read Committed는 Phantom Read, Unrepeatable Read가 발생하며, Read Uncommitted는 Phantom Read, Unrepeatable Read, Dirty Read가 발생할 수 있음.
한 트랜잭션 내에서 동일한 쿼리를 보냈을 때, 해당 조회 결과가 다른 경우.
예를 들어 사용자 A가 회원 테이블에서 age가 12 이상인 회원들을 조회하는 쿼리를 보내고 결과, 3명의 회원이 조회된다고 가정.
그 다음 사용자 B가 age가 15인 회원 레코드를 삽입.
이제 4명의 회원이 조회됨.
한 트랜잭션 내에서 결과값이 3->4로 변경되었음.
한 트랜잭션 내의 같은 레코드에 여러 번 조회가 발생했는데, 그 값이 다른 경우.
트랜잭션이 시작되고 1,000이었던 값이 업데이트 되어 다음 번에 2,000으로 읽힐 때를 예시로 들 수 있음.
같은 트랜잭션 내에서 동일한 레코드의 결과가 바뀌었기 때문에 문제.
Unrepeatable Read와 유사하며, 한 트랜잭션이 실행 중일 때, 다른 트랜잭션에 의해 수정되었지만 아직 커밋되지 않은 행의 데이터를 읽을 수 있을 때 발생.
예를 들어, A의 계좌에 1000원이, B의 계좌에 2000원이 있고, A가 B에게 1000원을 송금하는 트랜잭션 a가 진행 중일 때, 트랜잭션 a에서 A는 0원, B는 3000원을 가진 상태일 것.
그러나 아직 트랜잭션 a가 커밋 전인데
다른 트랜잭션 b에서 B의 계좌에서 3000원을 모두 인출하는 트랜잭션을 실행한다면?
트랜잭션 a가 롤백 될 때 문제가 발생할 것.
매우 엄격한 수준으로 트랜잭션이 발생한 해당 행을 격리시키고, 이후 동일한 행에 트랜잭션을 발생시키려면 이전 트랜잭션이 끝날 때까지 대기.
말 그대로 각 트랜잭션이 순차적으로 접근한다고 볼 수 있음
고로 교착 상태가 일어날 확률이 높고 가장 성능이 떨어지는 격리 수준.
하나의 트랜잭션이 수정한 행을 다른 트랜잭션이 수정할 수 없도록 막아줌.
그러나 새로운 행을 추가하는 것은 막지 않기 때문에 이후 추가된 행이 발견될 수 있음.
가장 많이 사용되는 격리 수준으로, MySQL, Postgres, SQL Server, Oracle의 기본값.
다른 트랜잭션이 커밋하지 않은 정보는 읽을 수 없음.
커밋 완료된 데이터에 대해서만 조회 허용.
그러나 현재 트랜잭션이 접근한 행을 다른 트랜잭션이 수정할 수 있음.
이 때문에 현재 트랜잭션이 같은 행을 다시 읽을 때, 다른 내용이 발견될 수 있음.
가장 낮은 격리 수준으로, 하나의 트랜잭션이 커밋되기 이전에 다른 트랜잭션에 노출되는 문제가 있음.
가장 빠르며 데이터 무결성을 위해 되도록 사용하지 않는 것이 이상적.
몇몇 행이 제대로 조회되지 않더라도 괜찮은 거대한 양의 데이터를 어림 잡아 집계하는 데는 사용 가능.
성공적으로 수행된 트랜잭션은 영구적으로 반영되어야 한다는 것.
DB에 시스템 장애가 발생해도 원래 상태로 복구하는 회복 기능이 있어야 한다는 것을 뜻함.
DB는 이를 위해 체크섬, 저널링, 롤백 등의 기능 제공
중복 검사의 한 형태로, 오류 정정을 통해 송신된 자료의 무결성을 보호하는 단순한 방법
파일 시스템, DB 시스템에 변경 사항을 커밋하기 전에 로깅하는 것.
트랜잭션 등 변경 사항에 대한 로그를 남기는 것.
데이터의 정확성, 일관성, 유효성을 유지하는 것을 말하며, 무결성이 유지되어야 DB에 저장된 데이터 값과 그 값에 해당하는 현실 세계의 실제 값이 일치하는지에 대해 신뢰할 수 있음.
무결성의 종류
개체 무결성
기본키로 선택된 필드는 빈 값을 허용하지 않음.
참조 무결성
서로 참조 관계에 있는 두 테이블의 데이터는 항상 일관된 값을 유지.
고유 무결성
특정 속성에 대해 고유한 값을 가지도록 조건이 주어진 경우, 그 속성 값은 모두 고유한 값을 가짐.
NULL 무결성
특정 속성 값에 NULL이 올 수 없다는 조건이 주어진 경우, 그 속성 값은 NULL이 될 수 없다는 제약 조건.