Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4 - 리브 #18

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
392 changes: 392 additions & 0 deletions DB/minjoo522.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,392 @@
### 데이터베이스에서 인덱스를 사용하는 이유와 장단점

- 인덱스 : 데이터베이스 테이블의 특정 컬럼에 대해 정렬된 구ㅗ를 제공해 검색 속도 향상 / 빠르게 원하는 데이터를 찾을 수 있다.
- 장점 : 검색 속도 향상, 정렬 효율
- 단점 : 쓰기 성능 저하(인덱스를 유지하기 위해 추가 연산 발생), 추가 저장 공간 필요

### InnoDB 인덱스 구조

- B+Tree 기반 구조
- 정렬된 키-값 구조
- 리프 노드들이 더블 링크드 리스트로 연결되어 있어 검색이나 순차적인 데이터 읽기에 유리하다. : 리프 노드들이 서로 앞뒤로 연결되어 있어 빠르게 순차적으로 접근할 수 있다.
- 균형 상태를 유지해 삽입, 삭제 후에도 일정한 성능을 보장한다.
- 클러스터형 인덱스(Primary key) / 보조 인덱스(Secondary Index)
- 클러스터형 인덱스(Clustered Index)
- 기본 키를 기준으로 데이터를 정렬하고 저장
- 기본 키가 없으면 내부적으로 6-byte의 숨겨진 클러스터 키를 생성해 이를 기반으로 데이터 저장 및 정렬
- 인덱스 노드 리프에는 실제 테이블의 행 데이터가 저장된다.
- 클러스터형 인덱스를 통해 데이터에 바로 접근할 수 있다.
- 테이블 당 하나만 만들 수 있다.
- 보조 인덱스(Secondary Index)
- 특정 컬럼에 대한 검색 기능 향상
- 리프 노트에 해당 컬럼 값 + 클러스터형 인덱스 키 값이 저장된다.
- 보조 인덱스를 사용한 후 클러스터형 인덱스를 통해 실제 데이터를 가져온다 : 백트래킹 / 커버링 인덱스
- 하나의 테이블에 여러개 생성할 수 있다.
- 장점
- 빠른 데이터 검색
- 효율적인 정렬 및 범위 검색
- 단점
- 쓰기 작업 비용 증가
- 추가 저장 공간

### InnoDB 버퍼 풀, REDO로그, UNDO로그

- 데이터베이스 성능과 데이터 무결성을 보장하는 중요한 역할을 한다.
- **InnoDB 버퍼 풀**
- 메모리 영역
- 디스크 I/O를 줄이고 데이터 읽기/쓰기 성능을 향상시키기 위해 사용된다.
- 데이터 페이지, 인덱스 페이지, 변경된 데이터를 디스크로 쓰기 전에 임시로 저장하는 캐시 역할
- 변경된 데이터를 모아서 일괄 처리로 디스크에 기록한다. -> 동일한 데이터를 여러 번 변경해도 디스크에 중복적으로 쓰지 않고 버퍼 풀에서만 갱신된다.
- 데이터 캐싱
- 자주 읽히는 데이터와 인덱스 페이지를 메모리에 유지해 디스크 접근 횟수를 줄인다.
- 쓰기 지연
- 데이터 변경은 즉시 디스크에 기록되지 않고, 버퍼 풀에 저장된 후 나중에 디스크에 기록 된다 -> 쓰기 성능 향상
- 데이터 변경 작업이 있을 때마다 디스크에 즉시 반영하면 쓰기 작업이 병목이 된다.
- 변경된 데이터는 **더티 페이지**로 표시된다 : 메모리에서 수정되었지만 아직 디스크에 반영되지 않은 페이지
- LRU 알고리즘
- 자주 사용되는 데이터를 오래 유지하기 위해 LRU(Least Recently Used) 알고리즘을 사용한다. -> 자주 사용되지 않는 데이터는 메모리에서 제거된다.
- **REDO 로그**
- 지속성을 보장하기 위한 로그 / 장애 복구를 위해 사용된다
- 커밋된 변경사항을 복구하거나 디스크에 기록되지 않은 변경 사항을 재적용한다.
- 어떤 데이터 페이지에서 어떤 부분이 어떻게 수정되었는지 기록한다.
- 쓰기 성능 향상
- 변경 사항은 데이터 파일에 직접 기록되지 않고, REDO 로그에 먼저 기록된 후, 비동기로 데이터 파일에 쓰여진다.
- 쓰기 작업 속도를 높이고, 디스크 I/O 부하를 줄인다.
- 복구 기능
- 장애 발생 -> REDO 로그를 이용해 마지막으로 커밋된 상태로 복구할 수 있다.
- WAL(Write-Ahead Logging) 방식
- 데이터를 디스크에 쓰기 전에 REDO 로그를 먼저 기록한다. -> 데이터 무결성 보장
- 고정 크기 순환 버퍼(circular buffer) : 로그 파일이 가득 차면 오래된 로그를 덮어쓴다.
- 디스크에 할당된 고정된 크기의 로그 파일로 저장된다.
- 덮어쓰기는 순차적으로 반복되며 계속 순환한다.
- **UNDO 로그**
- 롤백이나 MVCC(Multi-Version Concurrency Control, 다중 버전 동시성 제어)를 지원하기 위해 사용한다.
- 트랜잭션이 데이터 변경 전에 해당 **데이터 원본 값**을 기록한다.
- 롤백
- 트랜잭션 도중 오류가 발생하거나 명시적으로 롤백이 요청되면 UNDO 로그를 이용해 데이터를 변경 이전 상태로 복원한다.
- 변경 이전 데이터를 UNDO 로그에 저장하고, 이를 사용해 원래 데이터를 복구한다.
- MVCC 지원
- UNDO 로그에 이전 데이터 버전을 저장해 읽기 작업이 트랜잭션의 스냅샷을 사용할 수 있도록 한다.
- 지연된 변경 기록
- 트랜잭션이 완료되기 전까지는 UNDO 로그를 통해 변경 내용이 롤백될 수 있다. -> 데이터 파일에는 즉시 기록되지 않는다.

#### 데이터 변경 과정 (버퍼 풀, UNDO 로그, REDO 로그 병렬 처리)

- 데이터 변경 요청이 들어오면, InnoDB는 먼저 데이터를 메모리 **버퍼 풀**에서 변경한다.
- 변경 전에 원래 데이터를 **UNDO 로그**에 저장한다.
- 변경 내용은 **REDO 로그**에 기록되어 디스크로 최종 기록을 보장한다.
- 일정 주기 또는 체크포인트 시점에 변경된 데이터는 디스크로 플러시 된다.

### 트랜잭션이란?

- 데이터베이스에서 일련의 연속적인 작업들을 하나의 단위로 묶어서 처리하는 개념
- 데이터베이스의 정합성을 유지하고 작업의 원자성을 보장하기 위해서 사용된다.
- ACID
- 원자성(Atomicity)
- 트랜잭션 내의 모든 작업은 하나의 단위로 처리된다.
- 트랜잭션이 시작되면 모든 작업이 완료되거나, 하나라도 실패하면 모든 작업이 취소된다.
- 일관성(Consistency)
- 트랜잭션이 완료되면 데이터베이스는 일관된 상태로 유지된다.
- 트랜잭션이 실행되기 전과 후, 데이터베이스는 정의된 규칙을 충족하는 상태 + 데이터 무결성 보장 (**제약 조건 만족**)
- 격리성(Isolation)
- 각 트랜잭션은 서로 독립적으로 실행되어야 한다.
- 지속성(Durability)
- 트랜잭션이 완료되면 그 결과는 영구적으로 저장되어야 한다.

### 트랜잭션 격리 수준

- 데이터베이스에서 동시에 실행되는 트랜잭션들이 서로 간섭하지 않고 독립적으로 수행되도록 보장하는 방법
- 데이터의 일관성과 정확성을 유지하면서 여러 트랜잭션에 동시에 실행될 수 있도록 돕는다.
- 하나의 트랜잭션이 다른 트랜잭션의 작업에 영향을 미치는 경우를 의미한다.
- READ UNCOMMITTED
- 하나의 트랜잭션애서 수정한 데이터가 아직 커밋되지 않았더라도 다른 트랜잭션이 이를 읽을 수 있다.
- Dirty Read가 발생할 수 있다.
- READ COMMITTED
- 한 트랜잭션이 커밋한 후에만 다른 트랜잭션이 그 값을 읽을 수 있다.
- Non-Repeatable Read가 발생할 수 있다.
- REPEATABLE READ
- 트랜잭션이 시작된 이후 읽은 데이터는 변경되지 않으며 다른 트랜잭션이 이를 수정할 수 없다.
- 동일한 쿼리로 반복해서 조회할 경우 항상 같은 결과를 받는다.
- Phantom Read가 발생할 수 있다.
- SERIALIZABLE
- 가장 높은 격리 수준
- 트랜잭션이 순차적으로 실행되는 것처럼 등장한다.
- 한 트랜잭션이 완료될 때까지 다른 트랜잭션이 실행되지 않는다.
- 성능 저하 / 병목 현상이 발생할 수 있다.

### MySQL의 Repeatable Read에서의 Phantom Read

- MVCC, Next-Key Lock으로 Phantom Read를 방지한다.
- MVCC
- 언두로그를 통해 대부분의 Phantom Read 상황을 커버할 수 있다.
- `SELECT .. FOR UPDATE`(선택한 행들을 잠금, 갭 잠금)를 실행하면 Phantom Read가 발생한다 -> 언두 레코드에는 Lock을 걸 수 없기 때문에 현재 레코드의 값을 가져오기 때문이다.
- Next-Key Lock
- Gap Lock + Record Lock

### 데이터베이스 정규화란?

- 데이터베이스 설계에서 데이터를 중복 없이 효율적으로 저장하기 위한 과정
- 목표 : 데이터 중복을 최소화하고 데이터의 일관성과 무결성을 유지하기
- 데이터를 여러 개의 관련된 테이블로 나누는 방법을 사용한다.

#### 1차 정규형(1NF: First Normal Form)

- 테이블 내 모든 컬럼이 원자값을 가져야 한다.
- 각 컬럼에 들어가는 데이터는 더 이상 나눠지지 않는 단일값, 배열이나 리스트는 허용되지 않는다.
- 잘못된 예

| 학생 ID | 이름 | 전화번호 |
| ------- | ------ | ------------------ |
| 1 | 홍길동 | 010-1234, 010-5678 |
| 2 | 김영희 | 010-9876 |

- 올바른 예

| 학생 ID | 이름 | 전화번호 |
| ------- | ------ | -------- |
| 1 | 홍길동 | 010-1234 |
| 1 | 홍길동 | 010-5678 |
| 2 | 김영희 | 010-9876 |

#### 2차 정규형(2NF: Second Normal Form)

- 1NF를 만족 + 부분 함수 종속(Partial Dependency)을 제거하는 규칙
- 기본키에 대해 모든 비기본 컬럼이 전체 기본키에 종속, 기본키의 일부에만 종속되는 컬럼은 별도의 테이블로 분리해야 한다.
- 잘못된 예

| 학생 ID | 과목 ID | 교수 이름 |
| ------- | ------- | --------- |
| 1 | 101 | 김 교수 |
| 1 | 102 | 이 교수 |
| 2 | 101 | 김 교수 |

- (학생 ID, 과목 ID) 복합키 -> 교수 이름은 과목 ID에만 의존한다.
- 학생 ID는 교수 이름에 영향을 미치지 않기 때문에 학생 ID가 교수 이름에 의존하는 것은 **부분 함수 종속**이다.

- 올바른 예
- 학생 테이블

| 학생 ID | 과목 ID |
| ------- | ------- |
| 1 | 101 |
| 1 | 102 |
| 2 | 101 |

- 교수 테이블

| 과목 ID | 교수 이름 |
| ------- | --------- |
| 101 | 김 교수 |
| 102 | 이 교수 |

#### 3차 정규형(3NF: Third Normal Form)

- 2NF를 만족 + 이행적 함수 종속(Transitive Dependency)을 제거하는 규칙
- 기본키와 관계없는 컬럼은 다른 컬럼에 의존하지 않아야 한다.
- 모든 비기본 컬럼은 기본키에만 의존해야 한다.
- 잘못된 예

| 직원 ID | 이름 | 부서 | 부서장 |
| ------- | ------ | ------ | ------- |
| 1 | 홍길동 | 개발 | 김 부장 |
| 2 | 김영희 | 마케팅 | 이 과장 |

- 부서장 -> 부서 / 부서 -> 직원 ID
- 부서장은 직원 ID와 관계 없이 부서에 의존

- 올바른 예
- 직원 테이블

| 직원 ID | 이름 | 부서 |
| ------- | ------ | ------ |
| 1 | 홍길동 | 개발 |
| 2 | 김영희 | 마케팅 |

- 부서 테이블

| 부서 | 부서장 |
| ------ | ------- |
| 개발 | 김 부장 |
| 마케팅 | 이 과장 |

- 부서장은 부서에만 의존한다.

#### 보이스-코드 정규형(BCNF: Boyce-Codd Normal Form)

- 3NF를 만족 + 기본키가 아닌 속성이 다른 비기본 속성에 의존하지 않도록하는 규칙
- 모든 결정자가 후보키여야 한다.
- 잘못된 예

| 학생 ID | 과목 ID | 교수 이름 |
| ------- | ------- | --------- |
| 1 | 101 | 김 교수 |
| 2 | 101 | 김 교수 |

- 과목 ID가 교수 이름을 결정하고 있다 -> 과목 ID 기본키 ❌

- 올바른 예
- 과목 테이블

| 과목 ID | 교수 이름 |
| ------- | --------- |
| 101 | 김 교수 |

- 학생 테이블

| 학생 ID | 과목 ID |
| ------- | ------- |
| 1 | 101 |
| 2 | 101 |

#### 4차 정규형(4NF: Fourth Normal Form)

- 다중 값 종속(Multi-valued Dependency)을 제거하는 규칙
- 하나의 컬럼이 두 개 이상의 독립적인 값을 가질 수 없도록 한다.
- 잘못된 예

| 학생 ID | 전화번호 | 이메일 |
| ------- | -------- | ------------ |
| 1 | 010-1234 | student1@abc |
| 1 | 010-5678 | student1@xyz |

- 학생 ID에 대해 전화번호와 이메일이 독립적으로 여러 값을 가질 수 있다. -> 같은 테이블에서 관리하는 것은 4NF 위반

- 올바른 예
- 학생 테이블

| 학생 ID | 전화번호 |
| ------- | -------- |
| 1 | 010-1234 |
| 1 | 010-5678 |

- 학생 이메일 테이블

| 학생 ID | 이메일 |
| ------- | ------------ |
| 1 | student1@abc |
| 1 | student1@xyz |

#### 5차 정규형(5NF: Fifth Normal Form)

- 조인 종속성을 제거
- 테이블이 분해되어도 원래의 데이터를 다시 복원할 수 있어야 하며, 여러 테이블을 조인해도 데이터 손실이 없어야 한다.
- 잘못된 예

| 학생 ID | 과목 ID | 교수 ID | 교수 이름 |
| ------- | ------- | ------- | --------- |
| 1 | 101 | 201 | 김 교수 |
| 1 | 102 | 202 | 이 교수 |

- 학생 ID, 과목 ID, 교수 ID를 사용해 교수 이름을 결정

- 올바른 예
- 학생과목 테이블

| 학생 ID | 과목 ID |
| ------- | ------- |
| 1 | 101 |
| 1 | 102 |

- 과목교수 테이블

| 과목 ID | 교수 ID |
| ------- | ------- |
| 101 | 201 |
| 102 | 202 |

- 교수 테이블

| 교수 ID | 교수 이름 |
| ------- | --------- |
| 201 | 김 교수 |
| 202 | 이 교수 |

- 장점
- 데이터 중복 최소화 : 저장 공간 절약할 수 있다.
- 데이터 무결성 유지 : 일관성 + 무결성 보장
- 업데이트 이상 방지 : 데이터 수정 시 오류 가능성이 줄어든다.
- 단점
- 성능 저하 : 조인이 자주 발생 -> 조인 성능 저하
- 복잡성 증가 : 데이터 구조 복잡, 쿼리 작성 복잡

### Join이란?

- 두 개 이상의 테이블을 결합해 하나의 결과를 생성하는 SQL 연산
- 서로 다른 테이블에 분산된 데이터를 연결하고, 하나의 결과 세트로 만들 수 있다.

#### INNER JOIN

- 두 테이블 간 일치하는 데이터만 결합
- 공통된 값이 있는 행만 결과에 포함
- JOIN == INNER JOIN

#### LEFT JOIN

- 왼쪽 테이블의 모든 행을 반환하고 오른쪽 테이블과 일치하는 데이터가 있으면 그것을 포함
- 오른쪽 테이블에 일치하는 값이 없으면 해당 컬럼에 NULL이 반환된다.

#### RIGHT JOIN

- 오른쪽 테이블의 모든 행을 반환하고 왼쪽 테이블과 일치하는 데이터가 있으면 그것을 포함
- 왼쪽 테이블에 일치하는 값이 없으면 해당 컬럼에 NULL이 반환된다.

#### FULL JOIN

- 양쪽 테이블에서 일치하는 데이터는 결합하고 일치하지 않는 데이터는 각각 NULL로 반환
- 왼쪽 테이블과 오른쪽 테이블의 모든 행을 반환한다.

#### CROSS JOIN

- 두 테이블의 모든 가능한 조합을 반환한다.
- 첫 번째 테이블의 모든 행이 두 번째 테이블의 모든 행과 결합된다.
- WHERE절을 사용하지 않는다.

### RDBMS vs NoSQL

#### RDBMS

- 관계형 데이터베이스 관리 시스템
- 데이터를 테이블 형식으로 저장한다.
- 스키마에 따라 데이터 구조를 정의한다.
- 정규화가 중요하다 -> 중복 없이 효율적으로 관리하려면 여러 테이블로 분리해 데이터 저장
- 강력한 트랜잭션 처리와 데이터 일관성

#### NoSQL

- 비관계형 데이터베이스 시스템
- 문서, 키-값, 그래프, 컬럼 등 여러가지 형식으로 데이터를 저장할 수 있다.
- 데이터 구조를 자주 변경하거나 복잡한 관계를 저장하는 데 유리하다.
- 가용성과 확장성 중시

### CAP 정리(브루어 정리)

- 데이터베이스 시스템에서 일관성, 가용성, 파티션 허용성 사이에서 동시에 세 가지를 만족시킬 수 없다
- 데이터베이스 시스템을 설계할 때 제약을 명확히 알려준다.
- 어떤 특성을 우선시할지 결정하고 그에 따른 타협을 잘 관리할 수 있게 된다.
- 일관성
- 모든 노드에 저장된 데이터는 동일해야 한다.
- 가용성
- 모든 요청에 대해 응답을 제공해야 한다.
- 파티션 허용성
- 네트워크 분할이 발생해도 시스템이 계속 동작해야 한다.

#### 조합

- CA(일관성 + 가용성)
- 네트워크 파티션이 발생하지 않는 경우에만 동작한다.
- 분산 시스템에서는 파티션 허용이 중요한 요소이므로 실용적이지 않다.
- CP(일관성 + 파이션 허용성)
- 네트워크 파이션이 발생했을 때도 일관성 보장
- 일부 노드가 응답하지 않아도 시스템은 정상 동작해야 하므로 가용성을 희생할 수 있다.
- 일부 쿼리나 요청은 실패할 수 있지만 데이터 일관성은 보장된다.
- AP(가용성 + 파티션 허용성)
- 네트워크 파티션이 발생해도 계속해서 가용성을 제공하며 일부 데이터가 최신이 아닐 수 있다.

### 파티셔닝과 샤딩

- 대규모 데이터베이스 시스템에서 성능을 향상시키고 관리가 용이하도록 데이터를 나누는 방법

#### 파티셔닝

- 데이터를 여러 개의 분할된 파티션으로 나누는 기술
- 단일 데이터베이스 내에서 데이터를 나누는 방식 -> 데이터의 논리적 분할