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
39 changes: 39 additions & 0 deletions 심종한/[lecture 28] B tree/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# lecture 28 - B tree

## B tree를 왜 DB 인덱스로 사용하는가

### 컴퓨터 시스템

- CPU: 프로그램 코드가 실제로 실행되는 곳
- Main Memory(RAM): 실행 중인 프로그램의 코드와 코드 실행에 필요한 혹은 그 결과로 나온 데이터가 상주하는 곳
- Secondary Storage(SSD or HDD): 프로그램과 데이터가 영구적으로 저장되는 곳, 실행 중인 프로그램의 데이터 일부가 임시 저장되는 곳

### Secondary Storage

- 데이터를 처리하는 속도가 가장 느리다.
- 데이터를 저장하는 용량이 가장 크다.
- block 단위로 데이터를 읽고 쓴다.
- 내가 사용하지 않는 불필요한 데이터도 읽힐 수 있다.

### database

- DB는 Secondary Storage에 저장된다. 또한 최대한 적게 접근하는 것이 성능 면에서 좋다.
- block 단위로 읽고 쓰기 때문에 연관된 데이터를 모아서 저장하면 더 효율적으로 읽고 쓸 수 있다.

![alt text](<avl vs b tree.png>)

가질 수 있는 자녀 노드 수에 차이가 있어 탐색 횟수를 줄일 수 있다.
B tree를 사용하면 데이터를 찾을 떄 탐색 범위를 빠르게 좁힐 수 있다.

또한 block 단위로 데이터를 읽기 때문에 유효한 데이터를 읽을 수 있는 장점이 있다.

<p align="center">
<img src="./avl.png" alt="Image 1" width="300"/>
<img src="./btree.png" alt="Image 2" width="300"/>
</p>

### B tree 계열을 DB 인덱스로 사용하는 이유

- DB는 기본적으로 secondary storage에 저장된다.
- B tree index는 self-balancing BST에 비해 secondary storage 접근을 적게한다.
- B tree 노드는 block 단위의 저장 공간을 알차게 사용할 수 있다.
Binary file added 심종한/[lecture 28] B tree/avl vs b tree.png
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 심종한/[lecture 28] B tree/avl.png
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 심종한/[lecture 28] B tree/btree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
101 changes: 101 additions & 0 deletions 심종한/[lecture25] db 인덱스/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# lecture25 - db 인덱스

## first_name에 인덱스가 걸려있다면?

```sql
SELECT * FROM customer WHERE first_name = 'Minsoo';
```

- 풀 스캔보다 더 빠르게 찾을 수 있다.

## 인덱스

### 인덱스를 사용하는 이유

- 조건을 만족하는 튜플을 빠르게 조회하기 위해
- 빠르게 정렬(order by)하거나 그룹핑(group by)하기 위헤

### 예제

| id | name | team_id | backnumber |
| --- | ---- | ------- | ---------- |
| ... | ... | ... | ... |

- 이 쿼리는 name 컬럼에 player_name_idx라는 이름의 인덱스를 생성한다.
- 인덱스를 사용하면 name 컬럼을 기준으로 한 검색이 더 빠르게 수행될 수 있다.

```sql
SELECT * FROM player WHERE name = 'Sonny';

CREATE INDEX player_name_idx ON player (name);
```

<br/>

- team_id, backnumber는 player 테이블 각 선수 정보를 유니크하게 식별할 수 있다
- 유니크 인덱스를 사용하면 team_id와 backnumber의 조합이 테이블에서 유일하도록 보장된다.

```sql
SELECT * FROM player WHERE team_id = 105 and backnumber = 7;

CREATE UNIQUE INDEX team_id_backnumber_idx ON player (team_id, backnumber);
```

**즉 사용하는 쿼리에 맞춰 적절하게 인덱스를 걸어줘야 빠르게 처리할 수 있다.**

### 쿼리문이 사용하는 인덱스를 알아보는 방법

```sql
EXPLAIN SELECT * FROM player WHERE team_id = 105 and backnumber = 7;
```

쿼리문에서 사용하는 인덱스는 DBMS의 opimizer가 알아서 적절하게 인덱스를 선택한다.
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
쿼리문에서 사용하는 인덱스는 DBMS의 opimizer가 알아서 적절하게 인덱스를 선택한다.
쿼리문에서 사용하는 인덱스는 DBMS의 optimizer가 알아서 적절하게 인덱스를 선택한다.


만약 직접 쿼리문에서 사용될 인덱스를 지정하고 싶은 경우 아래 쿼리를 실행하면 된다.

```sql
# 권장 사항(반드시 사용한다는 것을 보장하지 않음)
SELECT * FROM player USE INDEX (backnumber_idx) WHERE backnumber = 7;

# 더욱 강제(하지만 해당 인덱스를 사용해서 조회가 불가능하면 `optimezer` 가 다른 방법을 선택한다)
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
# 더욱 강제(하지만 해당 인덱스를 사용해서 조회가 불가능하면 `optimezer` 가 다른 방법을 선택한다)
# 더욱 강제(하지만 해당 인덱스를 사용해서 조회가 불가능하면 `optimizer` 가 다른 방법을 선택한다)

SELECT * FROM player FORCE USE INDEX (backnumber_idx) WHERE backnumber = 7;
```

### Covering index

만약 team_id, backnumber 로 인덱스를 생성한 상태에서 아래 쿼리를 실행한다고 가정해보자.

```sql
SELECT team_id, backnumber FROM player WHERE team_id = 5;
```

`team_id` 를 기준으로 인덱스 테이블을 탐색하게 되고, 이후 실제 테이블을 찾아가 데이터를 조회하게 된다. 그러나 지금은 인덱스 정보에 `backnumber` 가 함께 저장되어 있어 굳이 실제 테이블에 접근할 필요가 없다.

이처럼 조회하는 속성을 인덱스가 모두 커버할 때 `커버링 인덱스`라고 부른다. 일종의 테크닉이다.

![alt text](<covering index.png>)

### Hash index

- hash table을 사용하여 인덱스를 구현
- O(1) 시간 복잡도
- rehashing에 대한 부담
- hash 테이블이 꽉 찬 경우 테이블의 크기를 늘리는 과정이 부담될 수 있음
- range 비교 불가능
- 정렬 정보가 없어 범위 검색이 불가능
- 인덱스 정보의 일부 칼럼 정보만으로 인덱스를 사용할 수 없음

### 풀 스캔이 더 좋은 경우

- 테이블에 데이터가 조금 있는 경우 (몇 십, 몇 백건 정도)
- 조회하려는 데이터가 테이블의 상당 부분을 차지하는 경우

### 주의사항

> [!NOTE]
>
> 1. 두 개 이상의 칼럼으로 구성된 인덱스를 `multicolumn index` 또는 `composite index` 라고 부른다.
> 2. MySQL에서 primary key에는 인덱스가 자동으로 생성된다.
> 3. MySQL에서 인덱스는 항상 정렬된 상태로 유지된다. 그리고 새로운 튜플이 추가되면 인덱스를 재구성하게 되며 저장 공간을 차지하는 등 인덱스 유지 비용이 발생한다.
> 4. 외래키는 인덱스가 자동으로 생성되지 않을 수 있다.
> 5. 이미 데이터가 몇 백만건 이상 있는 테이블에 인덱스를 생성하는 경우 해당 서버에 트래픽이 적은 시간대에 작업을 하는 것이 좋다. 작업이 오래 걸려 DB 성능이 저하될 수 있다.
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,73 @@
# lecture29 - partitioning, sharding, replication

## partitioning

### vertical partitioning

**칼럼을 기준으로 테이블을 나누는 방식**

예를 들어 게시판 글 목록을 조회하는 상황을 가정하자.

우린 게시글 본문 내용이 아닌 다른 정보만 쿼리를 날려 조회한다.

![alt text](<게시글 조회.png>)

그런데 실제로는 한 행을 모두 HDD 또는 SSD에서 데이터를 읽고 메모리에 올린다.

만약 content 데이터의 크기가 크다면 불필요한 I/O 작업이 발생할 수 있다.

이 문제를 해결하고자 vertical partitioning을 적용하여 아래 사진과 같은 구조로 테이블을 변경할 수 있다.

![alt text](<vertical partitioning.png>)

### horizontal partitioning

**행을 기준으로 테이블을 나누는 방식**

사용자 테이블, 채널 정보 테이블, 구독 정보를 저장하는 테이블이 있다고 가정하자.

사용자 수가 10,000명, 채널 수가 1,000개 있다고 하자. 구독 정보는 최대 100,00,000개가 생길 수 있다.

점점 구독 정보가 쌓이면서 테이블의 크기가 커지면 인덱스의 크기도 커진다. 또한 테이블에 읽기/쓰기 작업이 있을 때마다 인덱스 처리 시간도 늘어나게 된다.

이런 문제를 horizontal partitioning 을 사용하여 해결할 수 있다.

#### hash-based

사용자 id를 해시 함수에 대입하여 0과 1의 결과만 나오도록 한다.

그 후 0과 1로 테이블을 구분하여 구독 정보를 저장한다.

![alt text](<hash function.png>)

이때 기준이 되는 user_id를 `partition key` 라고 부른다.

그런데 이 상황에서 id가 1인 channel 정보를 조회하고 싶은 경우에 두 테이블을 모두 조회해야 한다.

따라서 아래 사항을 주의하며 사용해야 한다.

> [!IMPORTANT]
>
> 1. 가장 많이 사용될 패턴에 따라 partition key를 정하는 것이 중요하다.
> 2. 데이터가 균등하게 분배될 수 있도록 hash function을 잘 정의하는 것도 중요하다.
> 3. hash-based horizontal partitioning은 한 번 partition이 나눠져서 사용되면 이후에 partition을 추가하기 까다롭다.
>
> 이 외에도 range-based 방식 등 다양한 방법이 있다.

## shardinng

horizontal partitioning 처럼 동작한다. 그리고 각 partition이 독립된 DB 서버에 저장된다.

띠라서 DB 부하를 분산시킬 수 있다.

![alt text](sharding.png)
'
이때 분류의 기준이 되는 키를 `shard-key` 라고 부른다.

## replication

master, slave 구조로 DB를 나눈다.

읽기/쓰기 작업을 하다가 master DB에 장애 상황이 발생하는 경우 slave DB를 사용하여 서비스를 계속해서 제공할 수 있게 된다.

또한 읽기 작업은 slave DB로 돌려 master DB의 트래픽을 분산하는 용도로 사용할 수도 있다.
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 심종한/[lecture30] dbcp/DBCP 문제점1.png
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 심종한/[lecture30] dbcp/DBCP 문제점2.png
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 심종한/[lecture30] dbcp/DBCP.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
71 changes: 71 additions & 0 deletions 심종한/[lecture30] dbcp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# lecture30 - dbcp

## DBCP를 사용하지 않을 때 발생하는 문제점

애플리케이션에서 DB로 데이터를 조회할 때 TCP 기반으로 동작한다. 이는 높은 송수신 신뢰성을 보장하기 때문이다.

그런데 TCP 커넥션을 열고 닫는 작업이 반복되면 시간적인 비용이 발생한다.

## DBCP

### 동작 방식

애플리케이션이 동작을 시작했을 때 미리 DB 커넥션을 만들어 커넥션 풀을 구성한다.

따라서 DB에 접근해야 할 때 커넥션 풀에서 사전에 만들어 둔 커넥션을 가져와 동작하게 된다.

![alt text](DBCP.png)

### DBCP 설정 방식

#### DB 서버 설정

- max_connections: client와 맺을 수 있는 최대 커넥션 수

만약 max_connections, DBCP의 최대 커넥션 수도 4라면?

![alt text](<DBCP 문제점1.png>)

애플리케이션 서버에 트래픽이 몰려 과부하가 걸린 상황에서 분산 처리를 위해 다른 서버를 띄울 수 있다.

이떄 DB의 최대 커넥션 수가 4이므로 새로운 커넥션을 획득할 수 없는 문제가 발생할 수 있다.

- wait_timeout: 커넥션이 inactive할 때 다시 요청이 오기까지 얼마의 시간을 기다린 뒤에 close할 것인지를 결정

![alt text](<DBCP 문제점2.png>)

애플리키에션 서버 자체적으로 버그가 생겨 커넥션을 점유하고 있는 경우, DB 서버의 커넥션이 계속해서 연결된 상태로 유지될 수 있어 리소스 낭비로 이어질 수 있다.

그래서 wait_timeout 파라미터를 설정하여 리소스 낭비를 방지할 수 있다.

#### DBCP 설정

- minimumidle: 풀에서 유지하는 최소한의 idle 커넥션 수
- idle 커넥션 수가 minimumidle보다 작고, 전체 커넥션 수도 maximumPoolSize보다 작다면 신속하게 추가로 커넥션을 만들게 된다.
- maximumPoolSize: 풀이 가질 수 있는 최대 커넥션 수, idle + active 커넥션 수

**HikariCP는 공식적으로 minimumidle과 maximumPoolSize 값을 동일하게 사용할 것을 권장하고 있다.**

- maxLifeTime: 풀에서 커넥션의 최대 수명, maxLifeTime을 넘기고 idle인 경우 풀에서 커넥션을 바로 제거한다. 커넥션을 사용하고 있으면 풀로 반환이 된 후에 제거된다.

DB의 커넥션 time limit보다 몇 초 짧게 설정할 것을 권장한다.

![alt text](maxLifeTime.png)

- connectionTimeout: 풀에서 커넥션을 받기 위한 대기시간

트래픽이 몰려 커넥션 풀에서 사용 가능한 커넥션이 없는 경우 무한정 기다릴 수 없다. 이때 최대 대기시간을 connectionTimeout으로 관리한다.

## 적절한 커넥션 수를 찾기 위한 과정

- 모니터링 환경 구축(서버 리소스, 서버 스레드 수, DBCP 등)
- 백엔드 시스템 부하 테스트
- request per second: 단위 초당 몇개의 request를 처리할 수 있는가
- avg response time: request를 처리할 때 평균적으로 걸리는 시간
- 백엔드, DB 서버의 CPU, 메모리 등의 리소스 사용률을 확인한다.
- 서버 CPU 사용률이 높아진다 -> 분산 서버
- DB 부하 -> DB 복제, 샤딩 등
- thread per request 모델이라면 활성화된 스레드 수를 확인한다.
- 이후 DBCP의 커넥션 수를 확인한다.

![alt text](RPS&ART.png)
Binary file added 심종한/[lecture30] dbcp/RPS&ART.png
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 심종한/[lecture30] dbcp/maxLifeTime.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 40 additions & 0 deletions 심종한/[lecture31] NoSQL/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# lecture31 - NoSQL

## scale up

서버의 사양을 높여 처리량을 높이는 방법

## scale out

서버의 수를 늘려 트래픽을 분산하는 방법

master DB를 여러개 두는 multi-master, 샤딩 같은 방벙도 있다. 그런데 일반적으로 RDB는 scale-out에 유연한 DB는 아니다.

## 트랜잭션 & ACID

ACID를 보장하려다 보니 DB 서버의 성능에 영향을 준다.

## NoSQL이 각광받게 된 배경

2000년대 초, 중반 SNS 사용자가 많아지며 사용자들은 빠르게 글을 보고 싶어하는 요구가 생겼다. 또한 비정형 데이터를 저장해야될 필요성이 점점 커지며 NoSQL이 각광받게 됐다.

## NoSQL 특징

#### 장점

- 유연한 스키마: 스키마를 고정하지 않고 유연하게 정의할 수 있다. 따라서 데이터 모델이 자주 변경되거나 다양한 형식의 데이터를 처리할 때 유용하다.
- 중복 허용(join 회피): 주문 정보를 저장하는 경우 주문자 정보부터 상품 정보까지 한번에 저장할 수 있다. 그렇다 보니 최신 정보를 반영할 수 있도록 신경써야 한다.
- 사용자 정보가 변경된 경우, 주문 정보에 저장된 주문자 정보를 모두 수정해줘야 한다.
- 수평적 확장성: 서버 여러대로 하나의 클러스터를 구성하여 사용할 수 있다. join 작업이 거의 없기 때문에 스케일 아웃에 유리하다.

#### 단점

- ACID 일부를 포기하고 high-throughput, low-latency를 추구한다.
- 금융 시스템처럼 데이터의 일관성이 중요한 환경에서는 조심히 사용해야 한다.

## Redis

- key, value로 데이터를 관리한다.
- 메모리를 사용하다 보니 DB 자체로 사용하기 보다는 캐시 목적으로 사용하는 것 같다.
- 또한 여러가지 데이터 타입을 저장할 수 있다. (string, lists, sets, hashes, sorted, sets ...)
- 고가용성 (replication, automatic failover)이 보장된다.
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
- 고가용성 (replication, automatic failover)이 보장된다.
- 고가용성 (replication, automatic failover)을 지원한다.