Skip to content
Open
Show file tree
Hide file tree
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
86 changes: 86 additions & 0 deletions 심종한/[lecture19] mvcc (1부)/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# lecture19 - mvcc (1부)

## MVCC (Multiversion concurrency control)

MVCC는 커밋된 데이터만 읽는다.

### committed read

이 isolation 레벨로 데이터를 읽으면 read하는 시간을 기준으로 그전에 커밋된 데이터를 읽는다.

![alt text](<read committed.png>)

따라서 이 상태에서는 `tx2` 가 write한 50이 조회된다.

### repeatable read

tx 시작 시간 기준으로 그전에 커밋된 데이터를 읽는다.

![alt text](<repeatable read.png>)

따라서 이 상태에서는 10이 조회된다.

> [!NOTE]
>
> **어떤 isolation 레벨로 데이터를 읽는지는 DBMS마다 다르다.**

### serializable

SSI 기법이 적용된 MVCC로 동작한다.

### read uncommitted

MVCC는 커밋된 데이터를 읽기 때문에 이 레벨에서는 MVCC가 적용되지 않는다.

## MVCC 특징

- 데이터를 읽을 때 `특정 시점 기준` 으로 가장 최근에 `커밋` 된 데이터를 읽는다.
- _이때 특정 시점은 isolation 레벨에 따라 다르다._
- 데이터 변화 이력을 관리한다.
- 따라서 추가적인 저장 공간을 사용하게 된다.
- read, write는 서로 block하지 않는다.
- 동시에 처리할 수 있는 트랜잭션이 늘어나 성능이 더 좋아질 수 있다.

> [!NOTE]
>
> 특정 시점을 기준으로 커밋된 데이터를 읽는 것을 MySQL에서 `consistent read` 라고 부른다.

## MVCC에서 발생 가능한 문제

### read committed 레벨을 사용하는 경우

> 현재 예제는 PostgreSQL을 기반으로 설명한다.

![alt text](<read committed 문제.png>)

`tx1` 의 쓰기 작업의 결과가 사라지는 문제가 있다.

왜냐하면 `tx1` 이 x값애 쓰기 작업을 할때 락을 획득한다.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
왜냐하면 `tx1`x값애 쓰기 작업을 할때 락을 획득한다.
왜냐하면 `tx1`x값에 쓰기 작업을 할때 락을 획득한다.

또한 `tx2` 는 커밋된 시점의 x값을 읽으므로 50을 읽는다.
30을 입금하려 하지만 `tx1` 이 쓰기 락을 가지고 있으므로 대기하게 되고 결과적으로 `lost update` 문제가 발생한다.

### 해결법

tx2 의 isolation 레벨을 repeatable read 로 바꾸면 해결된다.

PostgreSQL 는 `repeatable read` 레벨을 사용하면 같은 데이터에 먼저 update한 tx가 커밋되었다면 이후 tx는 롤백하는 기능이 있다.

![alt text](<repeatable read 해결법.png>)

이런 특성을 `fist-updater-win` 라고도 한다.

### 더 고민해보자

일단 트랜잭션마다 서로 다른 isolation 레벨을 가져갈 수 있다.

그렇다면 tx1 이 read committed 레벨을 사용해도 정말 괜찮은지 보자.

![alt text](<read committed 문제점.png>)

이처럼 트랜잭션의 동작 순서가 바뀐다면 lost update 문제가 발생하게 된다.

**즉 서로 연관되는 트랜잭션의 isoloation 레벨을 동시에 고려해야 문제를 해결할 수 있다는 것이다.**

![alt text](<repeatable read로 바꿔도 문제.png>)

`tx1` 의 isolation 레벨을 바꾸더라도 `tx1` 은 롤백되는 상황이 발생할 수 있고 여전히 `lost update` 문제가 발생한다.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions 심종한/[lecture20] mvcc (2부)/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# lecture20 - mvcc (2부)

## MySQL에서 lost update 해결

### locking read

![alt text](<locking read1.png>)

MySQL에서는 select for update 구문을 사용할 수 있다.
단, `tx1` , `tx2` 모두 `locking read` 를 사용해야만 한다.
`tx1` 이 `locking read` 를 사용하지 db에 커밋되어 있는 값을 읽게 된다.

![alt text](<locking read2.png>)

또한 MySQL에서 locking read는 가장 최근에 커밋된 데이터를 읽는다.
따라서 repeatable read 레벨이더라도 트랜잭션의 시작 시점의 값을 읽지 않고 가장 최근에 커밋된 데이터를 읽도록 동작한다.

![alt text](<locking read3.png>)

### wrtie skew 문제

정상적으로 동작하면 결과는 다음과 같다.

![alt text](<repeatable read 정상.png>)

그러나 아래처럼 locking read를 사용하지 않으면 문제가 생긴다.

![alt text](<locking read 사용하지 않음.png>)

![alt text](<wrtie skew.png>)

#### MySQL에서의 해결 - select for update

![alt text](<mysql solution.png>)

MySQL에서 locking read를 사용하면 repeatable read 격리 수준이더라도 가장 최근에 커밋된 데이터를 읽게 되므로 문제가 해결된다.

#### PostgreSQL

그러나 PostgreSQL에서 repeatable read 격리 수준에서는 동작하는 방식이 달라 먼저 update한 tx가 커밋되면 이후 tx는 롤백된다.

![alt text](<postgresql rollback1.png>)

이번엔 select for share 로 읽기 잠금을 획득하더라도 동일한 이유로 롤백된다.

### 정리

DBMS의 종류에 따라 동작 방식이 다르므로 상황에 알맞은 방법을 적용해야 한다.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added 심종한/[lecture20] mvcc (2부)/wrtie skew.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# lecture 21 - db 테이블 설계 잘못하면 발생하는 문제

## 부서와 사원을 관리하는 테이블

| empl_id | empl_name | birth_date | position | salary | dept_id | dept_name | dept_leader_id |
| ------- | --------- | ---------- | -------- | ------ | ------- | --------- | -------------- |
| 1 | MESSI | ... | ... | ... | 1001 | DEV | 1 |
| 2 | JINHO | ... | ... | ... | 1001 | DEV | 1 |
| 3 | JENNY | ... | ... | ... | null | null | null |
| ? | ? | ? | ? | ? | 1002 | QA | null |

### 문제점

부서가 지정되지 않은 직원을 추가할 때, 직원이 없는 부서를 추가할 때 등의 상황에서 불편한 점이 많다.

데이터가 지정되지 않은 경우 null값을 사용해야 한다.
또한 어떤 부서에 직원이 추가될 때마다 기존 부서 저장용 행을 지우고 삽입해야 한다.

이상 현상이 발생할 수 있는 설계다.

- **수정 이상**
- 부서 이름을 수정하는 경우 모든 행을 수정하지 않는 경우 부서명이 달라지는 현상이 발생할 수 있다.
- **삽입 이상**
- 부서에 첫 직원 정보를 저장해야 할 때 기존 부서 정보를 저장하는 행을 제거한 후 직원 정보를 저장해야 한다.
- **삭제 이상**

## 테이블을 잘 설계하지 않는 경우

- 중복 데이터 문제
- spurious tupples
- null 값이 많아짐으로 인한 문제점
- null값이 있는 칼럼으로 조인하는 경우 예상 밖의 결과가 나올 수 있음
- null값이 있는 칼럼에 aggregate function을 사용했을 때 주의 필요
- 불필요한 저장공간 낭비
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# lecture22 - db 정규화의 근본 (functional dependency)

## functional dependency

`한 테이블에 있는 두 개의 속성 집합 사이의 제약` 을 의미한다.

### EMPLOYEE

| empl_id | empl_name | birth_date | position | salary | dept_id |
| ------- | --------- | ---------- | -------- | ------ | ------- |

#### 집합 X

- empl_id

#### 집합 Y

- empl_name
- birth_date
- position
- salary
- dept_id

X 값에 따라 Y 값이 유일하게 (uniquely) 결정될 때 `X가 Y를 함수적으로 결정한다 (functionally determine)`, `Y가 X에 함수적으로 의존한다 (functionally dependent)` 라고 말할 수 있고, 두 집합 사이의 이러한 제약 관계를 functional dependency (FD)라고 부른다.

> [!IMPORTANT]
>
> 테이블의 스키마를 보고 의미적으로 FD가 존재하는지 파악해야 한다.

## 함수적 종속 종류

### Trivial functional dependency

X -> Y 일 때, Y가 X의 부분집한인 경우

```
{ a, b, c } -> { c }
{ a, b, c } -> { a, c }
{ a, b, c } -> { a, b, c }
```

### Non-trivial functional dependency

X -> Y 일 때, Y가 X의 부분집합이 아닌 경우

```
{ a, b, c } -> { b, c, d}
{ a, b, c } -> { d, e } // 완벽히 겹치는 것이 없으므로 completely non-trivial FD이기도 함
```

### Partial functional dependency

X -> Y 일때, X의 진부분집합도 Y를 함수적으로 결정할 수 있는 경우

```
{ empl_id, empl_name } -> { birth_date } 이 가능하다면
{ empl_id } -> { birth_date } 도 가능하다.
```

### Full functional dependency

X -> Y 일때, 모든 X의 진부분집합으로 Y를 함수적으로 결정할 수 없는 경우

```
{ stu_id, class_id } -> { grade } 이 가능하다면
{ stu_id }, { class_id }, {} 만으로 { grade }를 결정할 수 없다.
```

## FD와 관련된 추가적인 개념

- Armstrong's axioms
- Closure
- minimal cover
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 55 additions & 0 deletions 심종한/[lecture23] db 정규화 (1NF, 2NF)/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# lecture23 - db 정규화 (1NF, 2NF)

## DB 정규화

데이터 중복과 삽입, 갱신, 삭제 이상을 최소화하기 위해 일련의 normal formms에 따라 관계형 DB를 수정하는 과정이다.

## 예시

- bank_name
- account_num
- account_id
- class
- ratio
- empl_id
- empl_name
- card_id

- super key: table에서 tuple들을 unique하게 식별할 수 있는 attributes set
- (candidate) key: 어느 한 attribute라도 제거하면 unique하게 tuples를 식별할 수 없는 super key
- { account_id }, { bank_name, account_num }
- primary key: table에서 tuple들을 unique하게 식별하려고 선택된 candidate) key
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- primary key: table에서 tuple들을 unique하게 식별하려고 선택된 candidate) key
- primary key: table에서 tuple들을 unique하게 식별하려고 선택된 candidate key

- { account_id }
- prime attribute: 임의의 key에 속하는 attribute
- account_id, bank_name, account_num
- non-prime attribute: 어떠한 key에도 속하지 않는 attribute
- class, ratio, empl_id, empl_name, card_id

## 1NF

**attribute의 값은 반드시 나눠질 수 없는 단일한 값이어야 한다.**

![alt text](1NF.png)

그러나 중복 데이터가 있고 PK도 변경해야 한다.

(candidate) key: { account_id, card_id }, { bank_name, account_num, card_id }
non-prime attribute: class, ratio, empl_id, empl_name

현재 상황에서 `{ account_id, card_id } -> { class, ratio, empl_id, empl_name }` FD가 있다.

그런데 `{ account_id } -> { class, ratio, empl_id, empl_name }` FD도 가능하다.

즉 모든 `non-prime attribute` 들이 `{ account_id, card_id }` 에 `partially dependent` 하다.

또한 `{ bank_name, account_num, card_id }` 에도 `partially dependent` 하다.

## 2NF

**모든 non-prime attribute는 모든 key에 fully functionally dependent 해야 한다.**

**다르게 말하면 key가 composite key가 아니라면 2NF는 자동적으로 만족한다고도 말할 수 있다. (일반적으로)**

이제 `card_id` 를 분리해야 한다.

![alt text](2NF.png)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# lecture24 - db 정규화 (3NF, BCNF, 역정규화)

![alt text](3NF.png)

`{ account_id } -> { empl_id }`

`{ empl_id } -> { empl_name }`

따라서 `{ account_id } -> { empl_name }` 함수적 종속이 존재한다.

`{ bank_name, account_num } -> { empl_id }` 이므로 `{ bank_name, account_num } -> { empl_name }` 함수적 종속이 존재한다.

## transitive functional dependency

`X -> Y & Y -> Z` 라면 `X -> Z` 도 함수적 종속이다. 단 Y, Z 모두 어떤 키에 대해서 부분집합이 아니여야 한다.

## 3NF

모든 non-prime attribute는 어떤 key에도 transitively dependent 하면 안된다.

![alt text](<3NF 이후.png>)

**3NF까지 되면 `정규화 됐다` 라고 말할 수 있다.**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**3NF까지 되면 `정규화 됐다` 라고 말할 수 있다.**
**실무에서는 일반적으로 3NF까지 되면 `정규화 됐다` 라고 말할 수 있다.**


## BCNF

모든 유효한 non-trivial FD X -> Y는 X가 super key여야 한다.

![alt text](BCNF.png)

![alt text](<BCNF 이후.png>)

더이상 bank_name이 중복되어 저장되는 일이 없다.