데이터베이스의 기본
- 데이터베이스는 일정한 규칙, 규약을 통해 구조화되어 저장되는 데이터의 모음이다.
- 해당 데이터베이스를 제어, 관리하는 통합 시스템을 DBMS라고 하며, 데이터 베이스 안에 있는 데이터들은 특정 DBMS마다 정의된 쿼리 언어를 통해 삽입, 삭제, 수정, 조회 등을 수행할 수 있다.
- 또한 데이터 베이스는 실시간 접근과 동시 공유가 가능하다.
엔터티(entity)
- 언터티는 사람, 장소, 물건, 사건, 개념 등 여러 개의 속성을 지닌 명사를 의미한다.
약한 엔터티와 강한 엔터티
- 엔터티는 약한 엔터티와 강한 엔터티로 나뉜다.
- 예를 들어 A가 혼자서는 존재하지 못하고 B의 존재 여부에 따라 종속적이라면 A는 약한 엔터티이고, B는 강한 엔터티이다.
릴레이션(relation)
- 데이터베이스에서 정보를 구분하여 저장하는 기본 단위이다.
- 엔터티에 관한 데이터를 데이터베이스는 릴레이션 하나에 담아서 관리한다.
- 릴레이션은 관계형 데이터 베이스에서는 테이블이라고 하며, NoSQL 데이터베이스에서는 컬렉션이라고 한다.
테이블과 컬렉션
- 데이터베이스의 종류는 크게 관계형 데이터베이스와 NoSQL 데이터베이스로 나눌 수 있다.
- 이 중 대표적인 관계형 데이터 베이스인 MySQL과 대표적인 NoSQL 데이터베이스인 MongoDB로 예시는 아래와 같다.
- MySQL : 레코드 - 테이블 - 데이터베이스
- MongoDB : 도큐먼트 - 컬렉션 - 데이터베이스
속성(attribute)
- 릴레이션에서 관리하는 구체적이며 고유한 이름을 갖는 정보이다.
- 예를 들어 ‘차’ 엔터티의 속성은 아래와 같다.
- 차 넘버, 바퀴 수, 차 색깔, 차종
도메인(domain)
- 도메인이란 릴레이션에 포함된 각각의 속성들이 가질 수 있는 값의 집합을 말한다.
- 예를 들어 성별이라는 속성이 있을 때 가질 수 있는 값은 [남, 녀]라는 집합이다.
필드와 레코드
- 데이터베이스에서 필드와 레코드로 구성된 테이블을 만들어 관리한다.
관계
- 여러 개의 테이블이 있고 이러한 테이블은 서로의 관게가 정의되어 있다. 이러한 관계를 관계 화살표라 한다.
1:1 관계
- 테이블을 두 개의 테이블로 나눠 테이블의 구조를 더 이해하기 쉽게 만들어 준다.
1:N 관계
- 한 개체가 다른 많은 개체를 포함하는 관계를 말한다.
N:M 관계
- N:M은 테이블 두 개를 직접적으로 연결해서 구축하지 않고, 1:N, 1:M이라는 관계를 갖는 테이블 두개로 나눠서 설정한다.
키
- 테이블 간의 관계를 조금 더 명확하게 하고 테이블 자체의 인텍스를 위해 설정된 장치로 기본키, 외래키, 후보키, 슈퍼키, 대체키가 있다.
- 슈퍼키는 유일성이 있고 그 안에 포함된 후보키는 최소성까지 갖춘 키이다.
- 후보키 중에 기본키로 선택되지 못한 키는 대체키가 된다.
- 유일성은 중복되는 값은 없으며, 최소성은 필드를 조합하지 않고 최소 필드만 써서 키를 형성할 수 있는 것을 말한다.
기본키(Primary Key)
- 기본키는 줄여서 PK라고 부르며, 유일성과 최소성을 만족하는 키다.
- 기본키는 자연키와 인조키 중에 골라 설정한다.
- 자연키 :중복된 값들을 제외하며 중복되지 않는 것을 ‘자연스레’뽑다가 나오는 키를 자연키라고 한다. 자연키는 언젠가는 변하는 속성을 가진다.
- 인조키 : 인위적으로 유저 아이디를 부여하여 생겨난 고유 식별자를 말한다. 자연키와 대도적으로 변하지 않는다. 따라서 보통 기본키는 인조키로 설정한다.
외래키(foreign key)
- 외래키는 FK라고도 하며, 다른 테이블의 기본키를 그대로 참조하는 값으로 개체와의 관계를 식별하는 데 사용한다.
ERD와 정규화 과정
- ERD(Entity Relationship Diagram)는 데이터베이스를 구축할 때 가장 기초적인 뼈대 역할을 하며, 릴레이션 간의 관계들을 정의한 것이다.
- 서비스를 구축할 때 가장 먼저 신경 써야 할 부분이다.
데이터베이스 이상 현상(anomaly)
- 데이터의 중복, 불일치, 손실 등으로 인해 데이터의 무결성이 깨지는 현상이다.
- 삽입 이상(Insertion Anomaly)
- 불필요한 데이터를 함께 삽입해야 하는 문제
- 예: 학생 정보를 저장할 때, 수강 신청 정보가 없어도 학과 정보를 반복해서 입력해야 하는 경우
- 삭제 이상(Deletion Anomaly)
- 튜플 삭제 시 연관된 다른 정보까지 함께 삭제되는 문제
- 예: 학생 정보를 삭제할 때, 해당 학생이 속한 학과의 유일한 학생이라면 학과 정보까지 손실되는 경우
- 수정 이상(Update Anomaly)
- 중복 데이터의 일부만 수정해 데이터 불일치가 발생하는 문제
- 예: 학과명을 변경할 때, 일부 튜플만 수정해 학과명이 일치하지 않게 되는 경우
정규화 과정
정규화 과정은 릴레이션 간의 잘못된 종속 관계로 인해 데이터베이스 이상 현상이 일어나서 이를 해결하거나, 저장 공간을 효율적으로 사용하기 위해 릴레이션을 여러 개로 분리하는 과정이다.
- 정규화 과정은 정규형 원칙을 기반으로 정규형을 만들어가는 과정이며, 정규화된 정도는 정규형(NF, Normal Form)으로 표현한다.
- 기본 정규형인 제1 정규형, 제2 정규형, 제3 정규형, 보이스/코드 정규형이 있고, 고급 정규형인 제4 정규형, 제5 정규형이 있다.
정규형 원칙
- 정규형의 원칙이란 같은 의미를 표현하는 릴레이션이지만 좀 더 좋은 구조로 만들어야 하고, 자료의 중복성은 감소해야 하고, 독립적인 관계는 별개의 릴레이션으로 표현해야 하며, 각각의 릴레이션은 독립적인 표현이 가능해야 하는 것을 말한다.
제1정규형(First Normal Form, 1NF):
- 모든 속성의 도메인이 원자값(atomic value)만을 포함해야 합니다. 즉, 각 속성은 더 이상 분해할 수 없는 단일 값을 가져야 한다.
- 같은 릴레이션에 중복된 튜플이 존재하지 않아야 한다.
- 각 튜플은 유일한 식별자(primary key)를 가져야 한다.
- 예를 들어, 학생 정보 테이블에서 ‘주소’ 속성이 ‘도시, 국가’와 같이 여러 값을 포함한다면 1NF를 만족하지 않는다. 이를 ‘도시’, ‘국가’로 분리해야 1NF를 충족할 수 있다.
제2정규형(Second Normal Form, 2NF):
- 1NF를 만족해야 한다.
- 모든 비주요 속성(non-prime attribute)이 후보 키(candidate key)에 완전 함수 종속(fully functional dependency)이어야 한다.
- 즉, 부분 함수 종속(partial functional dependency)이 존재하지 않아야 한다.
- 예를 들어, ‘학생, 과목, 교수, 성적’ 테이블에서 ‘성적’이 ‘학생, 과목’ 조합에 종속되고, ‘교수’가 ‘과목’에만 종속된다면 2NF를 만족하지 않는다. 이 경우 ‘학생, 과목, 성적’과 ‘과목, 교수’로 테이블을 분리해야 2NF를 충족할 수 있다.
제3정규형(Third Normal Form, 3NF):
- 2NF를 만족해야 한다.
- 모든 비주요 속성이 기본 키(primary key)에 이행적 함수 종속(transitive functional dependency)이 아니어야 한다.
- 즉, 비주요 속성 간의 종속 관계가 존재하지 않아야 한다.
- 예를 들어, ‘학생, 학과, 학과장’ 테이블에서 ‘학과장’이 ‘학과’에 종속되고, ‘학과’가 ‘학생’에 종속된다면 3NF를 만족하지 않는다. 이 경우 ‘학생, 학과’와 ‘학과, 학과장’으로 테이블을 분리해야 3NF를 충족할 수 있다.
보이스/코드 정규화(BCNF)
BCNF(Boyce-Codd Normal Form)는 제3정규형(3NF)보다 더 강력한 정규화 단계로, 함수 종속성(functional dependency)과 관련된 이상 현상을 추가로 제거한다.
BCNF의 조건:
- 릴레이션이 3NF를 만족해야 한다.
- 모든 결정자가 후보 키여야 한다. 즉, X → Y (X가 Y를 함수적으로 결정)인 함수 종속성에서 X는 반드시 후보 키여야 한다.
예시:
- 학생(학번, 이름, 학과, 지도교수)의 릴레이션이 있다고 가정한다. 이 릴레이션에는 다음과 같은 함수 종속성이 존재한다:
- 학번 → 이름, 학과, 지도교수
- 학과 → 지도교수
- 이 릴레이션은 3NF를 만족하지만, BCNF는 만족하지 않는다. 왜냐하면 ‘학과 → 지도교수’에서 ‘학과’가 후보 키가 아니기 때문이다.
이 경우 ‘학과’ 속성이 ‘지도교수’를 결정하므로, 이상 현상이 발생할 수 있다.
BCNF를 만족하기 위해서는 릴레이션을 분해해야 한다.:
- 학생(학번, 이름, 학과)
- 학과_지도교수(학과, 지도교수)
- 각 정규형은 이전 단계의 정규형을 포함하며, 차례로 만족해야 한다.
- 높은 단계의 정규형을 충족할수록 데이터의 중복이 감소하고 종속성이 줄어들어 이상 현상을 방지할 수 있다.
- 하지만 지나친 정규화는 테이블의 수를 증가시키고 조인 연산의 비용을 높일 수 있으므로, 상황에 맞게 적절한 수준의 정규화를 적용하는 것이 중요하다.
트랜잭션과 무결성
트랜잭션(transaction)
- 트랜잭션은 하나의 논리적 기능을 수행하기 위한 작업의 단위를 말한다.
- 데이터베이스에서는 여러 개의 쿼리들을 하나로 묶는 단위를 말한다.
- 이에 대한 특징은 원자성, 일관성, 독립성, 지속성이 있다. 이를 줄여서 ACID 특징이라고 한다.
원자성(atomicity)
- “All or Nothing”
- 트랜잭션과 관련된 일이 모두 수행되었거나 되지 않았거나를 보장하는 특징이다.
- 예를 들어 트랜잭션을 커밋했는데, 문제가 발생하여 롤백하는 경우 그 이후에 모두 수행되지 않음을 보장하는 것을 말한다.
- 트랜잭션 단위로 여러 로직들을 묶을 때 외부 API를 호출하는 것이 존재해서는 안된다.
- 만약 존재한다면 롤백이 일어났을 때 어떻게 해야 할 것인지에 대한 해결 방법이 있어야하고, 트랜잭션 전파를 신경써서 관리해야 한다.
커밋과 롤백
- 커밋(commit)은 여러 쿼리가 성공적으로 처리되었다고 확정하는 명령어이다.
- 트랜잭션 단위로 수행되며 변경된 내용이 모두 영구적으로 저장되는 것을 말한다.
- ‘커밋이 수행되었다’ -> ‘하나의 트랜잭션이 성공적으로 수행되었다.’
- 롤백(roll back)이란 트랜잭션으로 처리한 하나의 묶음 과정을 일어나기 전으로 돌리는 일(취소)를 말한다.
트랜잭션 전파
- 트랜잭션을 수행할 때 커넥션 단위로 수행하기 때문에 커넥션 객체를 넘겨서 수행해야 한다.
- 하지만 이를 매번 넘겨주기는 번거롭다. 때문에 여러 트랜잭션 관련 메서드의 호출을 하나의 트랜잭션에 묶이도록 하는 것을 트랜잭션 전파라고 한다.
일관성(consistency)
- 일관성은 ‘허용된 방식’으로만 데이터를 변경해야 하는 것을 의미한다.
- 데이터 베이스에 기록된 모든 데이터는 여러 가지 조건, 규칙에 따라 유효함을 가져야 한다.
격리성(isolation)
- 격리성은 트랜잭션 수행 시 서로 끼어들지 못하는 것을 말한다.
- 복수의 병렬 트랜직션은 서로 격리되어 마치 순차적으로 실행되는 것처럼 작동되어야 하고, 데이터 베이스는 여러 사용자가 같은 데이터에 접근할 수 있어야 한다.
- 하지만 단순하게 순차 처리를 하게 된다면 성능이 떨어지는 문제가 발생한다.
- 이러한 이유로 격리성은 여러개의 격리 수준으로 나뉘어 격리성을 보장한다.
격리 수준
- 트랜잭션의 격리 수준(Isolation Level)은 동시에 실행되는 트랜잭션 간의 데이터 일관성과 격리성을 결정하는 중요한 요소이다. 격리 수준은 낮은 수준에서 높은 수준까지 다음과 같이 분류된다.
- Read Uncommitted (레벨 0):
- 다른 트랜잭션에서 커밋되지 않은 데이터를 읽을 수 있다.
- Dirty Read, Non-Repeatable Read, Phantom Read 문제가 발생할 수 있다.
- 데이터 일관성이 보장되지 않아 거의 사용되지 않는다.
- Read Committed (레벨 1):
- 커밋된 데이터만 읽을 수 있습니다. Dirty Read는 방지된다.
- 트랜잭션 내에서 동일한 쿼리를 실행해도 결과가 다를 수 있다. (Non-Repeatable Read)
- 트랜잭션 도중 새로운 데이터가 삽입되어 쿼리 결과에 포함될 수 있다. (Phantom Read)
- 대부분의 DBMS에서 기본적으로 사용되는 격리 수준이다.
- Repeatable Read (레벨 2):
- 트랜잭션 내에서 한 번 읽은 데이터를 반복해서 읽어도 같은 결과를 보장한다. Non-Repeatable Read를 방지한다.
- 트랜잭션 도중 수정된 데이터는 트랜잭션이 시작되기 전의 상태로 보인다.
- 새로운 데이터가 삽입되는 Phantom Read 문제는 여전히 발생할 수 있다.
- MySQL의 InnoDB 스토리지 엔진에서 기본적으로 사용되는 격리 수준다.
- Serializable (레벨 3):
- 가장 높은 수준의 격리성을 제공합니다. Dirty Read, Non-Repeatable Read, Phantom Read를 모두 방지한다.
- 트랜잭션이 순차적으로 실행되는 것처럼 동작한다.
- 동시성이 크게 떨어지고 성능 저하가 발생할 수 있다.
데이터 일관성이 매우 중요한 경우에 사용된다. (예: 금융 트랜잭션)
- 각 격리 수준은 동시성과 데이터 일관성 사이의 트레이드오프 관계에 있다.
- 격리 수준이 높을수록 데이터 일관성은 보장되지만, 동시성이 떨어지고 성능이 저하될 수 있다.
반면, 격리 수준이 낮을수록 동시성은 향상되지만 데이터 일관성 문제가 발생할 수 있다.
- 적절한 격리 수준을 선택하는 것은 애플리케이션의 요구사항과 데이터 일관성의 중요성에 따라 결정해야 한다. 대부분의 경우 Read Committed나 Repeatable Read 수준으로 충분하지만, 데이터 일관성이 매우 중요한 경우에는 Serializable 수준을 고려할 수 있다.
격리 수준에 따라 발생하는 현상
- 팬턺 리드(phantom read) : 한 트랜잭션 내에서 동일한 쿼리를 보냈을 때 해당 조회 결과가 다른 경우이다.
- 반복 가능하지 않은 조회(non-repeatable read) : 한 트랜잭션 내의 같은 행에 두 번 이상 조회가 발생했는데, 그 값이 다른 경우를 가리킨다.
- 팬턺 리드와 다른 점은 반복 가능하지 않은 조회는 행 값이 달라질 수도 있는데, 팬텀 리드는 다른 행이 선택될 수 있다는 것을 의미한다.
- 더티 리드(dirty read) : 한 트랜잭션이 실행 중일 때 다른 트랜잭션에 의해 수정되었지만 아직 ‘커밋되지 않은’행의 데이터를 읽을 수 있을 때 발생한다.
지속성(durability)
- 성공적으로 수행된 트랜잭션은 영원히 반영되어야 하는 것을 의미한다.
- 이는 데이터베이스에 시스템 장애가 발생해도 원래 상태로 복구하는 회복 기능이 있어야 함을 뜻하며, 데이터베이스는 이를 위해서 체크섬, 저널링, 롤백 등의 기능을 제공한다.
- 체크섬 : 중복 검사의 한 형태로, 오류 정정을 통해 송신된 자료의 무결성을 보호하는 단순하 방법
- 저널링 : 파일 시스템 또는 데이터베이스 시스템에서 변경 사항을 반영(commit)하기 전에 로깅하는 것, 트랜잭션 등 변경 사항에 대한 로그를 남기는 것
무결성
- 무결성이란 데이터의 정확성, 일관성, 유효성을 유지하는 것을 말하며, 무결성이 유지 되어야 데이터베이스에 저장된 데이터 값과 그 값에 해당하는 현실 세계의 실제 값이 일치하는지에 대한 신뢰가 생긴다.
- 개체 무결성 : 기본키로 선택된 필드는 빈 값을 허용하지 않는다.
- 참조 무결성 : 서로 참조 관계에 있는 두 테이블의 데이터는 항상 일관된 값을 유지해야 한다.
- 고유 무결성 : 특정 속성에 대해 고유한 값을 가지도록 조건이 주어진 경우 그 속성 값은 모두 고유한 값을 가진다.
- NULL 무결성 : 특정 속성 값에 NULL이 올 수 어벗다는 조건이 주어진 경우 그 속성 값은 NULL이 될 수 없다는 제약 조건이다.
데이터베이스의 종류
관계형 데이터베이스
- 행과 열을 가지는 표 형식 데이터를 저장하는 형태의 데이터베이스를 가리키며 SQL이라는 언어를 써서 조작한다.
- MySQL, PostgreSQL, 오라클, SQL Server, MSSQL 등이 있다
- 참고로 관계형 데이터 베이스의 경우 표준 SQL을 지키긴 하지만, 각각의 제품에 특화시킨 SQL을 사용한다.
- 예를 들어 오라클의 경우 PL/SQL, SQL Server의 경우 T-SQL
MySQL
- 대부분의 운영체제와 호환되며 현재 가장 많이사용하는 데이터베이스이다.
- C, C++로 만들어졌으며 MyISAM 인덱스 압축 기술, B-트리 기반의 인덱스, 스레드 기반의 메모리 할당 시스템, 매우 빠른 조인, 최대 64개의 인덱스를 제공한다.
- 대용량 데이터베이스를 위해 설계되었고 롤백, 커밋, 이중 암호 지원 보안 등의 기능을 제공한다.
- 모듈식 아키텍처로 쉽게 스토리지 엔진을 바꿀 수 있으며 데이터 웨어하우징, 트랜잭션 처리, 고가용성 처리에 강점을 두고 있다.
- 스토리지 엔진 위에는 커넥터 API 및 서비스 계층을 통해 MySQL 데이터베이스와 쉽게 상호 작용할 수 있다.
- 쿼리 캐시를 지원하기 때문에 사용자가 작성한 쿼리가 캐시에 있는 쿼리와 동일하면 서버가 빠르게 응답해줄 수 있다.
PostgreSQL
- MySql 다음으로 개발자들이 선호하는 데이터베이스 기술로 널리 인정받았다.
- 디스크 조각이 차지하는 영역을 회수할 수 있는 장치인 VACUUM이 특징이다.
- 최대 테이블의 크기는 32TB이다.
- SQL뿐만 아니라 JSON을 이용해서 데이터에 접근할 수 있다.
- 지정 시간에 복구하는 기능, 로깅, 접근 제어, 중첩된 트랜잭션, 백업 등을 할 수 있다.
NoSQL 데이터 베이스
- SQL을 사용하지 않는 데이터베이스를 말한다. 대표적으로 MongoDB와 redis가 있다.
MongoDB
- JOSN을 통해 데이터에 접근할 수 있고, Binary JSON(BSON) 형태로 데이터가 저장된다.
- 와이어드 타이거 엔진이 기본 스토리지 엔진으로 장착된 키-값 데이터 모델에서 확장된 도큐먼트 기반의 데이터베이스이다.
- 확장성이 뛰어나며 빅데이터를 저장할 때 성능이 좋고, 고가용성과 샤딩, 레플리카셋을 지원한다.
- 스키마를 정해 놓지 않고 데이터를 삽입할 수 있기 때문에 다양한 도메인의 데이터베이스를 기반으로 분석하거나 로깅 등을 구현할 때 강점을 보인다.
- MongoDB는 도큐먼트를 생성할 때 마다 다른 컬렉션에서 중복값을 지니기 힘든 유니크한 값인 ObjectID가 생성된다.
- ID는 타임스탬프 + 랜덤값 + 카운터로 이루어져 있다
Redis
- 인메모리 데이터베이스이자 키-값 데이터 모델 기반의 데이터베이스이다.
- 기본적인 데이터 타입은 문자열이며 최대 512MB까지 저장할 수 있다.
- 이외에도 set, hash 등을 지원한다.
- pub/sub 기능을 통해 채팅 시스템, 다른 데이터베이스 앞단에 두어 사용하는 캐싱 계층, 단순한 키-값이 필요한 세션 정보 관리, 정렬된 셋(sorted set) 자료 구조를 이용한 실시간 순위표 서비스에 사용한다.
인덱스
- 인덱스는 데이터를 빠르게 찾을 수 있는 하나의 장치이다.
B-트리
- 인덱스는 보통 B-트리라는 자료 구조로 이루어져 있다.
- 이는 루트 노드, 리프 노드, 브랜치 노드(루트와 리프 노드 사이)로 나뉜다.
- 특징
- 각 노드는 여러 개의 키(key)와 자식 포인터를 가진다.
- 노드의 키 개수를 k라고 할 때, 자식 포인터의 개수는 k+1이다.
- 루트 노드를 제외한 모든 내부 노드는 최소 ⌈m/2⌉개의 키를 가진다. (m은 차수(order)로, 노드가 가질 수 있는 최대 키의 개수이다.)
- 루트 노드는 적어도 1개의 키를 가진다. (트리가 비어있지 않은 경우)
- 모든 리프 노드는 같은 레벨에 있다.
- 노드 내의 키는 오름차순으로 정렬되어 있다.
- 각 키의 왼쪽 서브트리에는 해당 키보다 작은 값이, 오른쪽 서브트리에는 해당 키보다 큰 값이 저장된다.
- 트리의 탐색은 맨 위 루트 노드부터 탐색이 일어나며 브랜치 노드를 거쳐 리프 노드까지 내려온다.
- 예를 들어 키 14를 찾을 경우
- ‘14보다 같거나 클 때까지’를 기반으로 처음 루트노드에서부터 내려와 리프노드인 14에 도달하는 것을 볼 수있다.
인덱스가 효율적인 이유와 대수확장성
- 인덱스가 효율적인 이유는 효율적인 단계를 거쳐 모든 요소에 접근할 수 있는 균형 잡힌 트리 구조와 트리 깊이의 대수확장성 때문이다.
- 대수확장성 : 트리 깊이가 리프 노드 수에 비해 매우 느리게 성장하는 것을 의미한다.
- 기본적으로 인덱스가 한 깊이씩 증가할 때마다 최대 인덱스 항목의 수는 4배씩 증가한다.
- 대략 depth 10으로 100만개의 레코드를 검색할 수 있다. 실제로는 더 효율적이다.
인덱스 만드는 방법
- 인덱스를 만드는 방법은 데이터베이스마다 다르며 MySQL과 MongoDB를 기준으로 설명한다.
MySQL
- MySQL의 경우 클러스터형 인덱스와 세컨더리 인덱스가 있으며, 클러스터형 인덱스는 테이블당 하나를 설정할 수 있다.
- primary key 옵션으로 기본키를 만들면 클러스터형 인덱스를 생성할 수 있고, 기본키로 만들지 않고 unique not null 옵션을 붙이면 클러스터형 인덱스로 만들 수 있다.
create index...
명령어 기반으로 만들면 세컨더리 인덱스를 만들 수 있다.- 하나의 인덱스만 생성할 것이라면 클러스터형 인덱스를 만드는 것이 세컨더리 인덱스를 만드는 것보다 성능이 좋다.
- 세컨더리 인덱스는 보조 인덱스로 여러 개의 필드 값을 기반으로 쿼리를 많이 보낼 때 생성해야 하는 인덱이다.
MongoDB
- MongoDB의 경우 도큐먼트를 만들면 자동으로 ObjectID가 형성되며, 해당 키가 기본키로 설정된다.
- 그리고 세컨더리키도 부가적으로 설정하여 기본키와 세컨더리키를 같이 쓰는 복합 인덱스를 설정할 수 있다.
인덱스 최적화 기법
- 인덱스 최적화 기법은 데이터베이스마다 대동소이하다.
- MongoDB를 기반으로 인덱스 최적화 기법을 설명한다.
- 인덱스는 비용이다.
- 인덱스는 두 번 탐색하도록 강요한다. 인덱스 리스트, 컬렉션 순으로 탐색을 하기 때문이며, 관련 읽기 비용이 들게 된다.
- 또한 컬렉션이 수정되었을 때 인덱스도 수정되어야 한다.
- 이때 B-트리의 높이를 균형 있게 조절하는 비용도 들고, 데이터를 효율적으로 조회할 수 있도록 분산시키는 비용도 들게 된다.
- 그렇기 때문에 쿼리에 있는 필드에 이덱스를 무작정 다 설정하는 것은 답이 아니다.
- 또한 컬렉션에서 가져와야 하는 양이 많을수록 인덱스를 사용하는 것은 비효율적이다.
- 항상 테스팅하라
- 인덱스 최적화 기법은 서비스 특징에 따라 달라진다.
- 서비스에서 사용하는 객체의 깊이, 테이블의 양 등이 다르기 때문이다.
- 그렇기 때문에 항상 테스팅하는 것이 중요하다.
explain()
함수를 통해 인덱스를 만들고 쿼리를 보낸 이후에 테스팅을 하며 걸리는 시간을 최소화 해야한다.
- 복합 인덱스는 같음, 정렬, 다중 값, 카디널리티 순이다.
- 보통 여러 필드를 기반으로 조회를 할 때 복합 인덱스를 생성하는데, 이 인덱스를 생성할 때는 순서가 있고 생성 순서에 따라 인덱스 성능이 달라진다.
- 같음, 정렬, 다중 값, 카디널리티 순으로 생성해야 한다.
equal
관련 쿼리가 있다면 제일 먼저 인덱스로 설정해야 한다.- 정렬에 쓰는 필드라면 그 다음 인덱스로 설정한다.
- 다중 값을 출력해야 하는 필드, 즉 쿼리 자체가 >, < 등 많은 값을 출력해야 하는 쿼리에 쓰는 필드라면 나중에 인덱스를 설정한다.
- 유니크한 값의 정도를 카디널리티라고 한다. 이 카디널리티가 높은 순서를 기반으로 인덱스를 생성해야 한다.
조인의 종류
- 조인(join)이란 하나의 테이블이 아닌 두 개 이상의 테이블을 묶어서 하나의 결과물을 만드는 것을 말한다.
- MySQL에서는 JOIN이라는 쿼리로, MongoDB에서는 lookup이라는 쿼리로 이를 처리할 수 있다.
- 참고로 MongoDB의 lookup은 관계형 데이터베이스보다 성능이 떨어진다.
- 따라서 조인 작업이 많을 경우 MongoDB보다는 관계형 데이터베이스를 써야한다.
- 내부 조인(inner join) : 왼쪽 테이블과 오른쪽 테이블의 두 행이 모두 일치하는 행이 있는 부분만 표기한다. (교집합)
- 왼쪽 조인(left outer join) : 왼쪽 테이블의 모든행이 결과 테이블에 표기된다.(오른쪽 테이블에서 일치하는 항목이 없다면 NULL 값이 포함됨)
- 오른쪽 조인(right outer join) : 왼쪽 조인의 반대 방향
- 합집합 조인(full outer join) : 두 개의 테이블을 기반으로 조인 조건에 만족하지 않는 행까지 모두 표기한다.
조인의 원리
- 조인들은 조인의 원리인 중접 루프 조인, 정렬 병합 조인, 해시 조인에 의해 일어난다.
- 앞서 설명한 조인의 종류는 이 원리를 기반으로 조인하는 것이다.
중첩 루프 조인(NLJ, Nested Loop Join)
- 중첩 for문과 같은 원리로 조건에 맞는 조인을 하는 방법이며, 랜덤 접근에 대한 비용이 많이 증가하므로 대용량의 테이블에는 사용하지 않는다.
- 예를 들어 ‘t1, t2 테이블을 조인한다’라고 했을 때 첫 번째 테이블에서 행을 한번에 하나 씩 읽고 그다음 테이블에서도 행을 하나씩 읽어 조건에 맞는 레코드를 찾아 결괏값을 반환한다.
- 참고로 중첩 루프 조인에서 발전하여 조인할 테이블을 작은 블록으로 나눠서 블록 하나씩 조인하는 블록 중첩 루프 조인(BNL)이라는 방식도 있다.
정렬 병합 조인
- 각각의 테이블을 조인할 필드 기준으로 정렬하고 정렬이 끝난 이후에 조인 작업을 수행하는 조인이다.
- 조인할 때 쓸 적절한 인덱스가 없고 대용량의 테이블들을 조인하고 조인 조건으로 <, > 등의 범위 비교 연산자가 있을 때 쓴다.
해시 조인
- 해시 조인은 해시 테이블을 기반으로 조인하는 방법이다.
- 두 개의 테이블을 조인한다고 했을 때 하나의 테이블이 메모리에 온전히 들어간다면 보통 중첩 루프 조인보다 더 효율적이다.
- 또한, 동등(=) 조인에서만 사용할 수 있다.
- MySQL의 해시 조인 단계는 빌드 단계, 프로브 단계로 나뉜다.
- 빌드 단계 : 입력 테이블 중 하나를 기반으로 메모리 내 해시 테이블을 빌드하는 단계이다.(더 작은 테이블을 빌드한다)
- 조인에 사용되는 필드가 해시 테이블의 키로 사용된다.
- 프로브 단계 : 레코드를 읽기 시작하며, 각 레코드에서 나머지 테이블의 id와 일치하는 레코드를 해시 테이블에서 찾아 결괏값을 반환한다.
- 이를 통해 각 테이블은 한 번씩만 읽게 되어 중첩루프 조인보다 성능이 더 좋다.
- 빌드 단계 : 입력 테이블 중 하나를 기반으로 메모리 내 해시 테이블을 빌드하는 단계이다.(더 작은 테이블을 빌드한다)