KEY 는 DB에서 레코드를 고유하게 식별하는 데 사용되는 속성 또는 속성들의 집합을 의미
Key의 종류
이름 | 설명 |
슈퍼 키 (Super Key) | 유일성을 만족하는 키, ex) 학번 + 이름, 주민번 |
복합 키 (Composite Key) | 2개 이상의 속성을 사용한 키 |
후보 키 (Candidate Key) | 유일성과 최소성을 만족하는 키, 기본키가 될 수 있는 후보이므로 후보 키라고 불림 |
기본 키 (Primary Key) | 후보 키에서 선택된 키, NULL값과 중복 허용 X |
대체 키 (Surrogate Key) | 후보 키 중에서 기본 키로 선택되지 않은 키 |
외래 키 (Foreign Key) | 어떤 테이블 간의 기본 키를 참조하는 속성 테이블 간의 관계를 나타내기 위해 사용 |
예시) 학생 테이블
학생ID | 이름 | 이메일 | 학번 | 반 | 번호 |
1 | 철수 | chulsoo@gmail.com | 20231001 | 3 | 12 |
2 | 영희 | younghee@gmail.com | 20231002 | 3 | 7 |
3 | 민수 | minsoo@gmail.com | 20231003 | 4 | 1 |
1. Primary Key (기본 키)
- 학생ID는 각 학생을 유일하게 구별할 수 있음
- 학생ID는 중복될 수 없고 절대 NULL 값을 가질 수 없음
- 테이블 마다 기본 키는 하나만 설정 가능
= 학생ID가 Primary Key
2. 후보 키 (Candidate Key)
- 학생ID, 이메일, 학번 모두 각각 학생을 유일하게 식별 가능
- 따라서 위 3개의 속성은 모두 후보 키가 됨
3. 대체 키 (Alternative Key)
후보 키 중에서 Primary Key가 아닌 것들, 즉 email
과 학번
이 대체 키
4. 외래 키 (Foreign Key)
성적 테이블
성적ID | 학생ID | 과목 | 점수 |
1 | 1 | 수학 | 90 |
2 | 2 | 영어 | 85 |
3 | 1 | 과학 | 88 |
- 성적 테이블의 학생ID = 학생 테이블의 학생ID
- 따라서 성적.학생ID는 학생.학생ID를 참조하는 외래 키임
- 이를 통해 두 테이블간 관계 설정
5. 복합 키 (Composite Key)
만일 성적 테이블에서 학생ID와 과목이 함께 하나의 PK라면 두 속성을 합쳐서 레코드 구분 가능
즉, 한 학생이 같은 과목 성적을 여러번 기록할 수 없도록 보장
6. 슈퍼 키 (Super Key)
학생 id
+email
로 유일하게 학생을 식별할 수 있음- 유일성을 보장하지만 불필요한 속성이 포함된 경우라 후보 키가 아님
👉 유일성을 만족하는 모든 키 조합이 슈퍼 키

인덱스 (Index)
데이터베이스에서 검색 속도를 빠르게 하기 위해 사용하는 자료 구조
책의 목차
나 색인과 같은 역할을 함
-- email 컬럼에 인덱스 생성
CREATE INDEX idx_email ON Users(email);
-- 복합 인덱스 (이름 + 나이)
CREATE INDEX idx_name_age ON Users(name, age);
예시
Users 테이블에 수만 명의 사용자가 있고, email을 기준으로 특정 사용자를 검색한다고 가정
SELECT * FROM Users WHERE email = 'chulsoo@gmail.com';
- 인덱스가 없으면?
→ 데이터베이스는 전체 테이블을 처음부터 끝까지 하나씩 확인 (Full Table Scan)
→ 느림 - 인덱스가 있으면?
→ 이메일 컬럼에 인덱스를 생성해두면
→ 마치 책에서 이름순 색인을 찾듯이, 빠르게 해당 이메일 위치로 이동
언제 인덱스를 써야 할까?
- WHERE 절에 자주 쓰이는 컬럼
- JOIN 조건에 자주 사용되는 컬럼
- 정렬(ORDER BY), 그룹(GROUP BY)에 자주 사용되는 컬럼
- 고유한 값을 자주 검색하는 컬럼 (ex. email, user_id 등)
📌 인덱스 주의 사항
데이터 추가, 수정, 삭제 시 인덱스도 같이 갱신 -> 쓰기 성능 저하
인덱스는 별도의 저장 공간 必
너무 많은 인덱스 사용은 성능 저하와 관리의 복잡성 유발
성능에 영향을 주는 요소
요소 | 설명 |
선택도(Selectivity) | 해당 컬럼이 얼마나 고유한 값을 가지는가 (고유할수록 성능에 유리) |
인덱스 컬럼 순서 | 복합 인덱스의 경우 조건 순서가 맞지 않으면 미사용 |
데이터 분포도 | 너무 같은 값이 많으면 인덱스 효율 ↓ |
쿼리 형태 | LIKE '%abc'처럼 앞이 와일드카드면 인덱스 무효 |
- 자주 검색하는 컬럼에 인덱스 부여
- 조건이 자주 조합되는 컬럼은 복합 인덱스
- 쓰기 많은 테이블에는 최소한의 인덱스만 유지
- EXPLAIN을 통해 쿼리 실행 계획을 확인하세요!
인덱스 구조
클러스터형 인덱스 (Clustered Index)
- 인덱스가 실제 데이터 자체를 정렬해서 저장 (ex. 영어 사전)
- 테이블 당 1개만 가능 (MySQL은 기본 키에 자동 생성)
데이터 = 인덱스
이므로 디스크 접근을 최소화하여 읽기가 빠름
ex) PRIMARY KEY 인덱스
보조 인덱스 (Secondary Index)
- 인덱스는 별도로 존재하고, 실제 데이터 위치만 참조
- 여러 개 생성 가능
- 읽기 시 추가로 데이터 위치를 따라 가야 함
- 인덱스 = PK + Row ID
ex) UNIQUE, 일반 B-Tree 인덱스
예시
- PRIMARY KEY를 만들면 그 컬럼이 Clustered Index가 됨
- 나머지 인덱스 (UNIQUE, 일반 INDEX)는 전부 보조 인덱스
CREATE TABLE Users (
id INT PRIMARY KEY, -- 클러스터형 인덱스 (기본키)
email VARCHAR(255),
name VARCHAR(100),
INDEX idx_email(email) -- 보조 인덱스
);
인덱스 종류
종류 | 설명 | 사용 예시 |
B-Tree 인덱스 | 기본 인덱스. 범위 검색, 정렬에 강함. 대부분의 RDB에서 기본 제공 |
숫자, 문 자열, 날짜 등 범용 컬럼 |
B+Tree 인덱스 | 데이터는 오직 리프 노드에만 저장, 리프 노드끼리 연결됨 | "" |
Hash 인덱스 | 해시 테이블 기반. 정확한 값 검색(=)에 빠름. 범위 검색 불가 | 메모리 기반 NoSQL(DB), Redis 등 |
Unique 인덱스 | 중복을 허용하지 않는 인덱스. 고유한 값만 허용 | 주민번호, 이메일, ID 등 |
Full-Text 인덱스 | 텍스트 검색 전용 인덱스. 단어 기준 검색, 유사 검색 등에 사용 | 게시판, 블로그 검색 |
Spatial 인덱스 | 공간(지리 정보, 좌표 등)을 효율적으로 검색하기 위한 인덱스 | 지도, 위치 기반 서비스 |
복합 인덱스 | 여러 컬럼을 묶어 하나의 인덱스로 생성. 조건이 복합적인 쿼리에 유리 |
WHERE name='홍길동' AND age=30 |
비트맵 인덱스 | 값이 적은(저카디널리티) 컬럼에 효과적. 0/1 비트 배열로 관리 | 성별, 상태값(예/아니오 등) |
Clustered 인덱스 | 인덱스가 데이터 자체를 정렬한 형태 (MySQL InnoDB에서 기본 PK 인덱스) |
PK(기본키)에 자동 생성 |
Non-clustered 인덱스 | 데이터와는 분리되어 별도로 인덱스만 존재하는 형태 | 서브 키, 조회 전용 컬럼 |
B-Tree?, B+Tree란?
MySQL의 DB engine인 InnoDB는 B+Tree로 이루어져 있는데, 이는 B-Tree의 확장 개념임
B-Tree
B-tree의 핵심은 데이터가 정렬된 상태로 유지되어 있음

가장 상단의 노드를 루트 노드, 중간을 브랜치 노드, 맨 아래는 리프 노드라고 칭한다.
B-tree는 Binary search tree와 유사하지만, 한 노드 당 자식 노드가 2개 이상 가능하다.
key 값을 이용해 찾고자 하는 데이터를 트리 구조를 이용해 찾는 것이다.
왜 B-Tree는 빠른가?
B-tree의 장점 한 가지는 '어떤 값에 대해서도 같은 시간에 결과를 얻을 수 있다'인데, 이를 '균일성'이라고 함
위의 예시에서 리프노드에 있는 값을 찾는 시간은 동일할 것이다.
(트리 높이가 다른 경우, 차이는 있겠지만 O(logN)이라는 시간 복잡도를 가짐)

균형 트리란 루트 노드부터 리프 노드까지의 거리가 일정한 트리 구조 의미
👉 트리 중에서 특시 성능이 안정화 됨
그러나 B-Tree는 처음 생성 당시는 균형트리지만, 테이블 갱신의 반복을 통해 서서히 균형이 깨지고 성능이 악화됨
어느 정도 자동으로 균형을 회복할 수 있지만,
갱신 빈도가 잦은 테이블에 작성되는 인덱스 같은 경우인덱스 재구성을 통해 트리의 균형을 되찾는 작업 必

풀 스캔이 테이블 크기에 비례하는 형태로 실행 시간이 늘어가는 데 반해
인덱스를 사용하면 실행 시간 저하는 보통 완만한 곡선을 그림
B+Tree
B-Tree의 확장 개념, B-Tree는 Internal 또는 Branch 노드에 key와 data를 담을 수 있음
하지만, B+Tree의 경우, 브랜치 노드에 key만 담아두고, data는 담지 않음
= 오직 리프 노드에만 key와 data를 저장하고, 리프 노드끼리 연결 리스트로 연결 되어 있음

InnoDB에서 B+tree는 단순하게 설명한 B+tree보다 더 복잡하게 구현돼 보임
- 같은 레벨의 노드들끼리는
Double Linked List
를 사용 - 자식 노드로는
Single Linked List
로 연결되어있다.
key의 범위마다 찾아가야할 페이지 넘버(포인터)가 있는데, 해당 페이지 넘버를 통해 곧바로 다음 노드로 넘어간다.
리프 노드에 다다랐을 때 디스크에 존재하는 데이터의 주소값을 구할 수 있고, Linked List를 통해 탐색도 가능하다.
항목 | B-Tree | B+Tree |
데이터 저장 위치 | 모든 노드(내부/리프)에 데이터 저장 가능 | 데이터는 오직 리프(Leaf) 노드에만 저장 |
리프 노드 연결 | 연결 안 됨 | 리프 노드끼리 연결 리스트로 연결 (범위 탐색에 유리) |
범위 조회 | 비효율적 | 효율적 (리프 노드만 따라가면 됨) |
사용성 | 일반 트리 검색용 | DB 인덱스에 최적화됨 (범위 검색, 정렬 등 빠름) |