diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture 28] B tree/README.md" "b/\354\213\254\354\242\205\355\225\234/[lecture 28] B tree/README.md" new file mode 100644 index 0000000..a600c16 --- /dev/null +++ "b/\354\213\254\354\242\205\355\225\234/[lecture 28] B tree/README.md" @@ -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]() + +가질 수 있는 자녀 노드 수에 차이가 있어 탐색 횟수를 줄일 수 있다. +B tree를 사용하면 데이터를 찾을 떄 탐색 범위를 빠르게 좁힐 수 있다. + +또한 block 단위로 데이터를 읽기 때문에 유효한 데이터를 읽을 수 있는 장점이 있다. + +

+ Image 1 + Image 2 +

+ +### B tree 계열을 DB 인덱스로 사용하는 이유 + +- DB는 기본적으로 secondary storage에 저장된다. +- B tree index는 self-balancing BST에 비해 secondary storage 접근을 적게한다. +- B tree 노드는 block 단위의 저장 공간을 알차게 사용할 수 있다. diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture 28] B tree/avl vs b tree.png" "b/\354\213\254\354\242\205\355\225\234/[lecture 28] B tree/avl vs b tree.png" new file mode 100644 index 0000000..93dd0e5 Binary files /dev/null and "b/\354\213\254\354\242\205\355\225\234/[lecture 28] B tree/avl vs b tree.png" differ diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture 28] B tree/avl.png" "b/\354\213\254\354\242\205\355\225\234/[lecture 28] B tree/avl.png" new file mode 100644 index 0000000..b64b64b Binary files /dev/null and "b/\354\213\254\354\242\205\355\225\234/[lecture 28] B tree/avl.png" differ diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture 28] B tree/btree.png" "b/\354\213\254\354\242\205\355\225\234/[lecture 28] B tree/btree.png" new file mode 100644 index 0000000..e21a7ad Binary files /dev/null and "b/\354\213\254\354\242\205\355\225\234/[lecture 28] B tree/btree.png" differ diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture25] db \354\235\270\353\215\261\354\212\244/README.md" "b/\354\213\254\354\242\205\355\225\234/[lecture25] db \354\235\270\353\215\261\354\212\244/README.md" new file mode 100644 index 0000000..3c1f031 --- /dev/null +++ "b/\354\213\254\354\242\205\355\225\234/[lecture25] db \354\235\270\353\215\261\354\212\244/README.md" @@ -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); +``` + +
+ +- 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가 알아서 적절하게 인덱스를 선택한다. + +만약 직접 쿼리문에서 사용될 인덱스를 지정하고 싶은 경우 아래 쿼리를 실행하면 된다. + +```sql +# 권장 사항(반드시 사용한다는 것을 보장하지 않음) +SELECT * FROM player USE INDEX (backnumber_idx) WHERE backnumber = 7; + +# 더욱 강제(하지만 해당 인덱스를 사용해서 조회가 불가능하면 `optimezer` 가 다른 방법을 선택한다) +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]() + +### Hash index + +- hash table을 사용하여 인덱스를 구현 +- O(1) 시간 복잡도 +- rehashing에 대한 부담 + - hash 테이블이 꽉 찬 경우 테이블의 크기를 늘리는 과정이 부담될 수 있음 +- range 비교 불가능 + - 정렬 정보가 없어 범위 검색이 불가능 +- 인덱스 정보의 일부 칼럼 정보만으로 인덱스를 사용할 수 없음 + +### 풀 스캔이 더 좋은 경우 + +- 테이블에 데이터가 조금 있는 경우 (몇 십, 몇 백건 정도) +- 조회하려는 데이터가 테이블의 상당 부분을 차지하는 경우 + +### 주의사항 + +> [!NOTE] +> +> 1. 두 개 이상의 칼럼으로 구성된 인덱스를 `multicolumn index` 또는 `composite index` 라고 부른다. +> 2. MySQL에서 primary key에는 인덱스가 자동으로 생성된다. +> 3. MySQL에서 인덱스는 항상 정렬된 상태로 유지된다. 그리고 새로운 튜플이 추가되면 인덱스를 재구성하게 되며 저장 공간을 차지하는 등 인덱스 유지 비용이 발생한다. +> 4. 외래키는 인덱스가 자동으로 생성되지 않을 수 있다. +> 5. 이미 데이터가 몇 백만건 이상 있는 테이블에 인덱스를 생성하는 경우 해당 서버에 트래픽이 적은 시간대에 작업을 하는 것이 좋다. 작업이 오래 걸려 DB 성능이 저하될 수 있다. diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture25] db \354\235\270\353\215\261\354\212\244/covering index.png" "b/\354\213\254\354\242\205\355\225\234/[lecture25] db \354\235\270\353\215\261\354\212\244/covering index.png" new file mode 100644 index 0000000..e8b7868 Binary files /dev/null and "b/\354\213\254\354\242\205\355\225\234/[lecture25] db \354\235\270\353\215\261\354\212\244/covering index.png" differ diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/README.md" "b/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/README.md" new file mode 100644 index 0000000..b3f82e0 --- /dev/null +++ "b/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/README.md" @@ -0,0 +1,73 @@ +# lecture29 - partitioning, sharding, replication + +## partitioning + +### vertical partitioning + +**칼럼을 기준으로 테이블을 나누는 방식** + +예를 들어 게시판 글 목록을 조회하는 상황을 가정하자. + +우린 게시글 본문 내용이 아닌 다른 정보만 쿼리를 날려 조회한다. + +![alt text](<게시글 조회.png>) + +그런데 실제로는 한 행을 모두 HDD 또는 SSD에서 데이터를 읽고 메모리에 올린다. + +만약 content 데이터의 크기가 크다면 불필요한 I/O 작업이 발생할 수 있다. + +이 문제를 해결하고자 vertical partitioning을 적용하여 아래 사진과 같은 구조로 테이블을 변경할 수 있다. + +![alt text]() + +### horizontal partitioning + +**행을 기준으로 테이블을 나누는 방식** + +사용자 테이블, 채널 정보 테이블, 구독 정보를 저장하는 테이블이 있다고 가정하자. + +사용자 수가 10,000명, 채널 수가 1,000개 있다고 하자. 구독 정보는 최대 100,00,000개가 생길 수 있다. + +점점 구독 정보가 쌓이면서 테이블의 크기가 커지면 인덱스의 크기도 커진다. 또한 테이블에 읽기/쓰기 작업이 있을 때마다 인덱스 처리 시간도 늘어나게 된다. + +이런 문제를 horizontal partitioning 을 사용하여 해결할 수 있다. + +#### hash-based + +사용자 id를 해시 함수에 대입하여 0과 1의 결과만 나오도록 한다. + +그 후 0과 1로 테이블을 구분하여 구독 정보를 저장한다. + +![alt text]() + +이때 기준이 되는 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의 트래픽을 분산하는 용도로 사용할 수도 있다. diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/hash function.png" "b/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/hash function.png" new file mode 100644 index 0000000..92ea223 Binary files /dev/null and "b/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/hash function.png" differ diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/sharding.png" "b/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/sharding.png" new file mode 100644 index 0000000..3d46f53 Binary files /dev/null and "b/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/sharding.png" differ diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/vertical partitioning.png" "b/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/vertical partitioning.png" new file mode 100644 index 0000000..4e3d596 Binary files /dev/null and "b/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/vertical partitioning.png" differ diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/\352\262\214\354\213\234\352\270\200 \354\241\260\355\232\214.png" "b/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/\352\262\214\354\213\234\352\270\200 \354\241\260\355\232\214.png" new file mode 100644 index 0000000..01b3e25 Binary files /dev/null and "b/\354\213\254\354\242\205\355\225\234/[lecture29] partitioning, sharding, replication/\352\262\214\354\213\234\352\270\200 \354\241\260\355\232\214.png" differ diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/DBCP \353\254\270\354\240\234\354\240\2201.png" "b/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/DBCP \353\254\270\354\240\234\354\240\2201.png" new file mode 100644 index 0000000..6f4ce2b Binary files /dev/null and "b/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/DBCP \353\254\270\354\240\234\354\240\2201.png" differ diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/DBCP \353\254\270\354\240\234\354\240\2202.png" "b/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/DBCP \353\254\270\354\240\234\354\240\2202.png" new file mode 100644 index 0000000..599c832 Binary files /dev/null and "b/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/DBCP \353\254\270\354\240\234\354\240\2202.png" differ diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/DBCP.png" "b/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/DBCP.png" new file mode 100644 index 0000000..b41713c Binary files /dev/null and "b/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/DBCP.png" differ diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/README.md" "b/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/README.md" new file mode 100644 index 0000000..6a1e007 --- /dev/null +++ "b/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/README.md" @@ -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]() + +애플리케이션 서버에 트래픽이 몰려 과부하가 걸린 상황에서 분산 처리를 위해 다른 서버를 띄울 수 있다. + +이떄 DB의 최대 커넥션 수가 4이므로 새로운 커넥션을 획득할 수 없는 문제가 발생할 수 있다. + +- wait_timeout: 커넥션이 inactive할 때 다시 요청이 오기까지 얼마의 시간을 기다린 뒤에 close할 것인지를 결정 + +![alt text]() + +애플리키에션 서버 자체적으로 버그가 생겨 커넥션을 점유하고 있는 경우, 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) diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/RPS&ART.png" "b/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/RPS&ART.png" new file mode 100644 index 0000000..9b5fa5a Binary files /dev/null and "b/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/RPS&ART.png" differ diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/maxLifeTime.png" "b/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/maxLifeTime.png" new file mode 100644 index 0000000..05e4f8c Binary files /dev/null and "b/\354\213\254\354\242\205\355\225\234/[lecture30] dbcp/maxLifeTime.png" differ diff --git "a/\354\213\254\354\242\205\355\225\234/[lecture31] NoSQL/README.md" "b/\354\213\254\354\242\205\355\225\234/[lecture31] NoSQL/README.md" new file mode 100644 index 0000000..2384fd9 --- /dev/null +++ "b/\354\213\254\354\242\205\355\225\234/[lecture31] NoSQL/README.md" @@ -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)이 보장된다.