diff --git "a/Computer_Science_Note/4\354\236\245_\353\215\260\354\235\264\355\204\260\353\262\240\354\235\264\354\212\244.md" "b/Computer_Science_Note/4\354\236\245_\353\215\260\354\235\264\355\204\260\353\262\240\354\235\264\354\212\244.md" new file mode 100644 index 0000000..aa2ad13 --- /dev/null +++ "b/Computer_Science_Note/4\354\236\245_\353\215\260\354\235\264\355\204\260\353\262\240\354\235\264\354\212\244.md" @@ -0,0 +1,504 @@ +# 4장. 데이터베이스 + +## 4.1 데이터베이스의 기본 + +- 일정한 규칙, 혹은 규약을 통해 구조화되어 저장되는 데이터의 모음. + +- DBMS(DataBase Management System): 해당 데이터베이스를 제어, 관리하는 통합 시스템 + +- 데이터베이스 안에 있는 데이터들은 특정 DBMS마다 정의된 쿼리 언어(query language)를 통해 CRUD를 수행한다. + +- 실시간 접근과 동시 공유가 가능하다. + +```mermaid + +flowchart TD + +A(응용 프로그램: Node.js, php 등) <--> B(DBMS: MySQL 등) +B <--> C(데이터베이스) +``` + +### 4.1.1 엔터티(entity) + +- 여러 개의 속성을 지닌 명사 + + - 회원(엔터티): 이름(속성), 아이디, 주소, 전화번호 + +- 서비스의 요구 사항에 맞춰 속성이 정해진다. + +#### 약한 엔터티와 강한 엔터티 + +- A는 B의 존재 여부에 따라 종속적일 때의 강한 엔터티(B), 약한 엔터티(A) + +### 4.1.2 릴레이션(relation) + +- 데이터베이스에서 정보를 구분하여 저장하는 기본 단위 + +- 데이터베이스는 엔터티에 관한 데이터를 릴레이션 하나에 담아서 관리한다. + +- 관계형 데이터베이스에서는 '테이블', NoSQL 데이터베이스에서는 '컬렉션'으로 불린다. + +#### 테이블과 컬렉션 + +| RDBMS | NoSQL | +| -------------------- | ---------------------- | +| MySQL 등 | MongoDB 등 | +| 레코드 - 테이블 - DB | 도큐먼트 - 컬렉션 - DB | + +- 레코드가 쌓여서 테이블이, 테이블이 쌓여서 DB가 된다. + +### 4.1.3 속성(attribute) + +- 릴레이션에서 관리하는 구체적이며 고유한 이름을 갖는 정보 + +- 서비스의 요구 사항을 기반으로 관리해야 할 필요가 있는 속성들만 엔터티의 속성이 된다. + +### 4.1.4 도메인(domain) + +- 릴레이션에 포함된 각각의 속성들이 가질 수 잇는 값의 집합 + +- 성별(속성)이 가지는 값(집합): [남, 여] + +### 4.1.5 필드와 레코드 + +- 필드: 데이터 속성 + +- 레코드, 튜플: 테이블에 쌓이는 행(row) 단위의 데이터 + +#### 필드 타입 + +| 필드 타입 | 예 | +| --------- | ----------------------------------------------------------------------------------- | +| 숫자 타입 | TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT | +| 날짜 타입 | DATE(3byte), DATETIME(8byte), TIMESTAMP(4byte) | +| 문자 타입 | CHAR(0~255 사이 고정 길이), VARCHAR(0~65,535 사이 가변 길이), TEXT, BLOB, ENUM, SET | + +### 4.1.6 관계 + +| | | +| -------- | -------------------------------------------------------------------------- | +| 1:1 관계 | 테이블을 두 개의 테이블로 나눠 테이블의 구조를 더 이해하기 쉽게 만든다 | +| 1:N 관계 | 한 개체가 다른 많은 개체를 포함하는 관계 | +| N:M 관계 | 직접 연결이 아닌 1:N, 1:M이라는 관계를 갖는 테이블 두 개로 나눠서 설정한다 | + +### 4.1.7 키 + +- 테이블 간의 관계를 조금 더 명확하게 하고 테이블 자체의 인덱스를 위해 설정된 장치 + +| | | +| ------------------------ | --------------------------------------------------------- | +| 기본키(PK, Primary Key) | 유일성, 최소성, 자연키 또는 인조키 중에 설정 | +| 자연키 | 중복된 값을 제외하며 중복되지 않는 것을 뽑는다, 변동성 | +| 인조키 | 인위적으로 생성한 키, 불변성: 주로 기본키로 설정 | +| 외래 키(FK, Foreign Key) | 다른 테이블의 기본키 참조, 개체와의 관계 식별, 중복 가능 | +| 후보키(Candidate Key) | 유일성과 최소성 만족: 기본키 후보 | +| 대체키(Alternate Key) | 후보키가 두 개 이상일 경우, 기본키로 지정되지 않은 후보키 | +| 슈퍼키(Super Key) | 각 레코드를 유일하게 식별할 수 있는 유일성을 갖춘 키 | + +## 4.2 ERD(Entity Relationship Diagram)와 정규화 과정 + +- 릴레이션 간의 관계 정의: 데이터베이스 구축 시 가장 기초적인 뼈대 역할 + +- 서비스를 구축할 때 가장 먼저 신경 써야 할 부분이다. + +### 4.2.1 ERD의 중요성 + +- 시스템의 요구 사항을 기반으로 작성(설계도 역할)되어 데이터베이스를 구축한다. + +- 관계형 구조로 표현할 수 있지만 비정형(비구조화) 데이터를 충분히 표현할 수 없다. + +### 4.2.2 예제로 배우는 ERD + +### 4.2.3 정규화(NF, Normal Form) 과정 + +- 데이터베이스 이상 현상(릴레이션 간 잘못된 종속 관계 등)시, 저장 공간을 효율적으로 사용하기 위해 릴레이션을 여러 개로 분리하는 과정 + +- 정규형 원칙을 기반으로 정규형을 만들어가는 과정 + +| | | +| ----------- | --------------------------------------------------------- | +| 기본 정규형 | 제 1 정규형, 제 2 정규형, 제 3 정규형, 보이스/코드 정규형 | +| 고급 정규형 | 제 4 정규형, 제 5 정규형 | + +- 테이블을 나누게 되면(정규화 과정) 조인 과정으로 인해 오히려 느려질 수 있다: 서비스에 따라 진행해야 한다. + +#### 정규형 원칙 + +- 같은 의미를 표현하는 릴레이션 + +- 좀 더 좋은 구조 + +- 자료의 중복성 감소 + +- 독립적인 관계는 별개의 릴레이션으로 표현 + +- 각각의 릴레이션은 독립적인 표현 가능 + +#### 제 1 정규형 + +- 릴레이션의 모든 도메인이 더 이상 분해될 수 없는 원자 값(atomic value)만으로 구성되어야 한다. + +- 릴레이션의 속성 값 중에서 한 개의 기본키에 대해 두 개 이상의 값을 가지는 반복 집합이 있어서는 안된다. + +#### 제 2 정규형 + +- 릴레이션: 제 1 정규형 + 부분 함수의 종속성 제거 + +- 부분 함수의 종속성 제거: 기본키가 아닌 모든 속성이 기본키에 완전 함수 종속적이다. + +- 릴레이션으로 분해할 때 동등한 릴레이션으로 분리, 정보 손실이 발생하지 않도록 분해되어야 한다. + +#### 제 3 정규형 + +- 릴레이션: 제 2 정규형 + 모든 속성이 이행적 함수 종속(transitive FD) 불만족 + + - 이행적 함수 종속: A -> B, B -> C라면 A -> C 성립되는데, 이는 집합 C가 집합 A에 이행적으로 함수 종속이 되었다. + +#### 보이스/코드 정규형(BCNF) + +- 릴레이션: 제 3 정규형 + 결정자가 후보키가 아닌 함수 종속 관계를 제거하여 릴레이션의 함수 종속 관계에서 모든 결정자가 후보키이다. + + - 결정자: 함수 종속 관계에서 특정 종속자(dependent)를 결정짓는 요소 + + - X -> Y일 때 X는 결정자, Y는 종속자이다. + +## 4.3 트랙잭션과 무결성 + +### 4.3.1 트랜잭션 + +- 데이터베이스에서 하나의 논리적 기능을 수행하기 위한 작업의 단위 + +- 쿼리를 통해 데이터베이스에 접근한다: 여러 개의 쿼리를 하나로 묶는 단위 + +#### 원자성(Atomicity) + +- all or nothing + +- 트랜잭션과 관련된 일이 모두 수행되거나 되지 않는 것을 보장한다. + +- 트랜잭션 단위로 여러 로직을 묶을 때 외부 API를 호출하는 것이 있으면 안된다.(롤백시 주의) + +| commit | rollback | +| ----------------------------------------------------------- | ---------------------------------------- | +| 여러 쿼리의 성공적인 처리 확정 | 트랜잭션으로 처리한 과정을 이전으로 돌림 | +| 트랜잭션 단위로 수행 | | +| 변경된 내용 모두 영구 저장 | | +| 커밋이 수행되었다 = 하나의 트랜잭션에 성공적으로 수행되었다 | | +| update, insert, delete | | + +- 데이터의 무결성을 보장한다. + +- 트랜잭션 전파: 여러 트랜잭션 관련 메서드의 호출을 하나의 트랜잭션에 묶이도록 한다. + +#### 일관성(Consistency) + +- '허용된 방식'으로만 데이터를 변경한다. + +- 데이터베이스에 기록된 모든 데이터는 여러 가지 조건, 규칙에 따라 유효성을 가져야 한다. + +#### 격리성(Isolation) + +- 트랜잭션 수행 시 서로 끼어들 수 없다. + +- 복수의 병렬 트랜잭션은 서로 격리되어 순차적으로 실행되는 것처럼 동작한다. + +- DB는 여러 사용자가 같은 데이터에 접근할 수 있어야 한다. + +- 격리 수준에 따라 격리성을 보장한다. + +| | | | | | +| ---------------- | --------- | ----------------------- | --------- | ------------------------ | +| READ_UNCOMMITTED | 팬텀 리드 | 반복 가능하지 않은 조회 | 더티 리드 | 강한 동시성, 약한 격리성 | +| READ_COMMITTED | 팬텀 리드 | 반복 가능하지 않은 조회 | | | +| REPEATABLE_READ | 팬텀 리드 | | | +| SERIALIZABLE | | | | 약한 동시성, 강한 격리성 | + +##### 격리 수준에 따라 발생하는 현상 + +- 팬텀 리드(Phantom Read): 한 트랜잭션 내에 동일한 쿼리를 보냈을 때 해당 조회 결과가 다른 경우 | + +- 반복 가능하지 않은 조회(Non-Repeatable Read): 한 트랜잭션 내의 같은 행에 두 번이상 조회가 발생했는데 그 값이 다른 경우 + +- 더티 리드(Dirty Read): 한 트랜잭션에 실행 중일 때 다른 트랜잭션에 의해 수정되었지만 아직 '커밋되지 않은' 행의 데이터를 읽을 수 있을 때 발생 + +##### 격리 수준 + +- SERIALIZABLE + + - 말 그대로 트랜잭션을 순차적으로 진행한다. + + - 여러 트랜잭션이 동시에 같은 행에 접근할 수 없다. + + - 매우 엄격한 수준으로 해당 행에 격리시키고 트랜잭션이 이 행에 대해 일어나면 기다려야 한다. + + - 교착 상태가 일어날 확률도 많고 가장 성능이 떨어진다. + +- REPEATABLE_READ + + - 하나의 트랜잭션이 수정한 행을 다른 트랜잭션이 수정할 수 없도록 막아준다. + + - 새로운 행을 추가하는 것은 막지 않으므로 이후에 추가된 행이 발견될 수도 있다. + +- READ_COMMITTED + + - MySQL 8.0, PostgreSQL, SQL Serever, 오라클에서 기본값으로 설정된, 가장 많이 사용되는 격리수준이다. + + - REPEATABLE_READ와는 달리 다른 트랜잭션이 커밋하지 않은 정보는 읽을 수 없습니다. + + - 커밋 완료된 데이터에 대해서만 조회를 허용합니다. + + - 어떤 트랜잭션이 접근한 행을 다른 트랜잭션이 수정할 수 있습니다. + +- READ_UNCOMMITTED + + - 가장 낮은 격리 수준이다. + + - 하나의 트랜잭션이 커밋되기 이전에 다른 트랜잭션에 노출되는 문제가 있지만 가장 빠르다. + + - 데이터 무결성을 위해 되도록이면 사용하지 않는 것이 이상적이나, 몇몇 행이 제대로 조회되지 않더라도 괜찮은 거대한 양의 데이터를 '어림잡아' 집계하는 데 사용하면 좋다. + +#### 지속성(Durability) + +- 성공적으로 수행된 트랜잭션은 영원히 반영되어야 한다. + +- 데이터베이스에 시스템 장애가 발생해도 원래 상태로 복구하는 회복 기능이 있어야 한다. + +- DB는 이를 위해 체크섬, 저널링, 롤백 등의 기능을 제공한다. + + - 체크섬: 중복 검사의 한 형태, 오류 정정을 통해 송신된 자료의 무결성을 보호하는 단순한 방법 + + - 저널링: 파일 시스템 또는 DB 시스테에 변경 사항을 반영(commit)하기 전에 로깅하는 것, 트랜잭션 등 변경 사항에 대한 로그를 남기는 것 + +### 4.3.2 무결성 + +- 데이터의 정확성, 일관성, 유효성을 유지하는 것 + +- DB에 저장된 데이터 값과 그 값에 해당하는 현실 세계의 실제 값이 일치하는지에 대한 신뢰가 생긴다. + +| | | +| ----------- | ------------------------------------------------------------------------------------------------ | +| 개체 무결성 | 기본키로 선택된 필드는 빈 값을 허용하지 않습니다. | +| 참조 무결성 | 서로 참조 관계에 있는 두 테이블의 데이터는 항상 일관된 값을 유지해야 한다. | +| 고유 무결성 | 특정 속성에 대해 고유한 값을 가지도록 조건이 주어진 경우 그 속성 값은 모두 고유한 값을 가집니다. | +| NULL 무결성 | 특정 속성 값에 NULL이 올 수 없다는 조건이 주어진 경우 그 속성 값은 NULL이 될 수 없다. | + +## 4.4 데이터베이스의 종류 + +### 4.4.1 관계형 데이터베이스(RDBMS) + +- 행과 열을 가지는 표 형식의 데이터를 저장하는 형태로 SQL 언어로 조작한다. + +#### MySQL + +- C, C++로 만들어진, 현재 가장 많이 사용하는 데이터베이스 + +- MyISAM 인덱스 압축 기술, B-트리 기반의 인덱스, 스레드 기반의 메모리 할당 시스템, 매우 빠른 조인, 최대 64개의 인덱스를 지원한다. + +- 대용량 데이터베이스를 위해 설계, 롤백, 커밋, 이중 암호 지원 보안 등 기능을 제공한다. + +- 스토리지 엔진: 데이터베이스의 심장 + + - 모듈식 아키텍처로 쉽게 스토리지 엔진을 바꿀 수 있다. + + - 데이터 웨어하우징, 트랜잭션 처리, 고가용성 처리에 강점을 두고 있다. + +- 스토리지 엔진 위에는 커넥터 API 및 서비스 계층을 통해 MySQL 데이터베이스와 쉽게 상호작용할 수 있다. + +- 쿼리 캐시 지원: 입력된 쿼리 문에 대한 전체 결과 집합을 저장한다. + + - 사용자가 작성한 쿼리가 캐시에 있는 쿼리와 동일하면, 서버는 캐시의 출력만 표시한다. + +#### PostgreSQL + +- MySQL 다음으로 개발자들이 선호하는 데이터베이스 + +- VACUUM: 디스크 조각이 차지하는 영역을 회수할 수 있는 장치 + +- 32TB로 최대 테이블 크기가 매우 크며, SQL뿐만 아니라 JSON으로도 데이터에 접근할 수 있다. + +- 지정 시간에 복구하는 기능, 로깅, 접근 제어, 중첩된 트랜잭션, 백업 등을 할 수 있다. + +### 4.4.2 NoSQL(Not only SQL) 데이터베이스 + +- SQL을 사용하지 않는 데이터베이스 + +#### MongoDB + +- wiredTiger 엔진이 기본 스토리지 엔진으로 장착된 키-값 데이터 모델에서 확장된, 도큐먼트 기반 데이터베이스 + +- JSON을 통해 데이터에 접근, BJSON(Binary JSON) 형태로 데이터가 저장된다. + +- 확장성이 뛰어나며 빅데이터 저장시 성능이 좋고 고가용성과 샤딩, 레플리카 셋을 지원한다. + +- 스키마를 정해 놓지 않고 데이터 삽입 가능: 다양한 도메인의 데이터베이스를 기반으로 분석, 로깅할 때 유용하다. + +- 도큐먼트 생성시, PK인 ObjectID(유닉스 시간 기반 타임스탬프(4byte), 랜덤 값(5byte), 카운터(3byte))가 생성된다. + +#### redis + +- 키-값 데이터 모델 기반의 인메모리 데이터베이스 + +- 기본 데이터 타입은 문자열(string)으로 최대 512MB까지 저장할 수 있다. + +- set, hash 등을 지원한다. + +- pub/sub 기능을 통해 채팅 시스템, 다른 데이터베이스 앞단에 두어 사용하는 캐싱 계층, 단순한 키-값이 필요한 세션 정보 관리, 정렬된 셋(sorted set) 자료 구조를 이용한 실시간 순위표 서비스에 사용한다. + +## 4.5 인덱스 + +### 4.5.1 인덱스의 필요성 + +- 데이터를 빠르게 찾을 수 있는 장치 + +### 4.5.2 B-트리 + +- 보통 B-트리(루트 노드, 리프 노드, 브랜치 노드) 구조로 이루어져 있다. + +#### 인덱스가 효율적인 이유와 대수 확장성 + +- 대수확장성: 트리 깊이가 리프 노트 수에 비래 매우 느리게 성장한다. + +- 기본적으로 인덱스가 한 깊이씩 증가할 때마다 최대 인덱스 항목 수는 4배씩 증가한다. + +| 트리 길이 | 인덱스 항목 수 | +| --------- | -------------- | +| 3 | 64 | +| 4 | 258 | +| 5 | 1,024 | +| 6 | 4,096 | +| 7 | 16,384 | +| 8 | 65,536 | +| 9 | 262,144 | +| 10 | 1,048,576 | + +- 트리 깊이가 10개라면, 100만개의 레코드를 검색할 수 있다.(실제 인덱스는 위 표보다 훨씬 더 효율적이다) + +### 4.5.3 인덱스를 만드는 방법 + +#### MySQL + +- 클러스터형 인덱스: 테이블당 하나를 설정할 수 있다. + + - PK 옵션으로 기본키 생성: 클러스터형 인덱스를 생성한다. + + - 기본키를 만들지 않고 unique not null 옵션: 클러스터형 인덱스를 만들 수 있다. + + - 하나의 인덱스만 생성한다면 클러스터형 인덱스를 만드는 것이 성능 면에서 좋다. + +- 세컨더리 인덱스: `create index ~` 명령어를 기반으로 생성하는 보조 인덱스 + + - 여러 개의 필드 값을 기반으로 쿼리를 많이 보낼 때 생성한다. + +#### MongoDB + +- 도큐먼트 생성시, 자동으로 ObjectID가 만들어지면서 자동으로 PK로 설정된다. + +- 세컨더리 키도 부가적으로 설정해서 기본키와 세컨더리 키를 같이 사용하는 복합 인덱스를 설정할 수 있다. + +### 4.5.4 인덱스 최적화 기법 + +#### 1. 인덱스는 비용이다. + +- 쿼리에 있는 필드에 인덱스를 전부 설정하거나 컬렉션에서 가져와야 하는 양이 많을수록 인덱스는 비효율적이다. + +#### 2. 항상 테스팅하라. + +- `explain()` 함수를 통해 인덱스를 만들고 쿼리를 보낸 후, 테스트하며 소요 시간을 최소화해야 한다. + +```sql +EXPLAIN +SELECT * FROM t1 +JOIN t2 ON t1.c1 = t2.c1 +``` + +#### 3. 복합 인덱스는 같음, 정렬, 다중 값, 카디널리티 순이다. + +- 인덱스 생성 순서에 따라 인덱스 성능이 달라진다. + +1. 동등(==, equal) 쿼리에 가장 먼저 인덱스로 설정한다. + +2. 정렬에 쓰는 필드에 다음으로 인덱스로 설정한다. + +3. 다중 값을 출력(비교 연산 등)하는 쿼리는 나중에 인덱스를 설정한다. + +4. 카디널리티(유니크한 값의 정도)가 높은 순서를 기반으로 인덱스를 생성한다. + +## 4.6 조인의 종류 + +- 하나의 테이블이 아닌 두 개 이상의 테이블을 묶어서 하나의 결과물을 만든다. + +- 여러 테이블을 조인하는 작업이 많은 경우: MongoDB lookup의 성능이 떨어지므로 RDB(MySQL JOIN)를 사용한다. + +| 조인 종류 | 정의 | +| ---------------------------- | --------------------------------------------------------- | +| 내부 조인(inner join) | 두 테이블의 행이 모두 일치하는 부분만 표기한다. | +| 왼쪽 조인(left join) | 왼쪽 테이블의 모든 행이 결과 테이블에 표기한다. | +| 오른쪽 조인(right join) | 오른쪽 테이블의 모든 행이 결과 테이블에 표기한다. | +| 합집합 조인(full outer join) | 두 테이블의 조인 조건에 만족하지 않는 행 전부를 표기한다. | + +## 4.7 조인의 원리 + +### 4.7.1 중첩 루프 조인(NLJ, Nested Loop Join) + +- 중첩 for 문 원리: 랜덤 접근에 대한 비용이 많이 증가하므로 대용량 테이블에서는 사용하지 않는다. + +```js +for each row in t1 matching reference key { + for each row in t2 matching reference key { + if row satisfies join conditions, send to client + } +} +``` + +- 블록 중첩 루프 조인(BNL, Block Nested Loop): 조인할 테이블을 작은 블록으로 나눠 블록 하나씩 조인한다. + +### 4.7.2 정렬 병합 조인 + +- 각각의 테이블을 조인할 필드 기준으로 정렬, 정렬이 끝난 이후에 조인 작업을 수행한다. + +- 조인할 때 쓸만한 인덱스가 없고 대용량 테이블을 조인한다. + +- 범위 비교 연산자(<, >) 조인에서 사용한다. + +### 4.7.3 해시 조인 + +- 해시 테이블을 기반으로 하나의 테이블이 메모리에 온전히 들어가면 보통 NLJ보다 효율적이다. + +- 동등(=) 조인에서만 사용할 수 있다. + +#### MySQL의 해시 조인 단계 + +##### 빌드 단계 + +- 입력 테이블 중 하나(바이트가 더 작은)를 기반으로 메모리 내 해시 테이블을 빌드하는 단계 + +- 조인에 사용되는 필드가 해시 테이블의 키로 사용된다. + +```mermaid +flowchart LR + +A(데이터베이스: persons, countries) --> |hash: contries.country_id| B(인메모리 해시 테이블) +``` + +##### 프로브 단계 + +- 레코드 읽기 시작: 각 레코드에서 `persons.country_id`에 일치하는 레코드를 찾아서 결괏값으로 반환한다. + +```mermaid +flowchart TD + +A(데이터베이스: persons, countries) --- |hash: person.country_id| B(인메모리 해시 테이블) +B --> |해시 테이블을 기반으로 일치하는 레코드 찾기| C(결괏값) +``` + +- 각 테이블을 한 번씩만 읽는다: 보통 중첩 루프 조인보다 성능이 좋다. + +- 시스템 변수 `join_buffer_size`에 의해 사용 가능한 메모리양을 제어하며 런타임 시에 조정할 수 있다. + +## 예상질문 + +1. 데이터베이스는 무엇인가요? + +2. 중첩 루프 조인이 무엇인가요? + +3. 인덱스를 매 필드마다 설정하는 것이 좋을까요?