I/O
- I/O(Input/Output)는 컴퓨터 시스템에서 데이터를 입력받고 출력하는 작업을 의미
- I/O 과정은 시스템 성능에 중요하며, 대량 데이터를 처리하는 애플리케이션에는 효율적인 I/O 관리가 필요
I/O 종류
순차 I/O
- 데이터를 디스크의 연속된 위치에서 순서대로 읽고 쓰는 방식
- 디스크의 헤드 이동 없이 연속적인 접근이 가능해 처리량(Throughput) 높음
- 대규모 데이터 읽기/쓰기 작업에 적합
예시:
- 데이터베이스에서 풀 테이블 스캔(Full Table Scan)
- 테이블의 모든 데이터를 조회하는 상황
랜덤 I/O
- 데이터가 디스크의 임의의 위치에 분산되어 있어, 여러 위치를 이동하며 데이터를 읽고 쓰는 방식
- 디스크의 헤드 이동(Seek) 시간이 많아 오버헤드 발생
- 작은 데이터를 빈번히 처리할 때 주로 발생
예시:
- WHERE(조건)이 포함된 쿼리를 통해 조회 및 수정 및 삭제하는 상황
- 인덱스 레인지 스캔
순차 I/O의 경우 한 번의 시스템 호출로 여러 데이터를 기록하며, 반면 랜덤 I/O는 각 데이터마다 별도의 시스템 호출이 필요하므로 효율이 떨어짐
따라서, 성능 최적화를 위해 랜덤 I/O를 최소화하고, 즉, 쿼리를 처리시 꼭 필요한 데이터만 읽도록 쿼리를 개선 하는 것을 의미
인덱스
인덱스(Index)란?
- 데이터를 빠르게 찾기 위해 특정 컬럼을 기준으로 미리 정렬해놓은 표
- 책의 목차처럼, 원하는 데이터를 바로 찾아갈 수 있도록 위치 정보를 제공
인덱스 장단점
장점
- 데이터 조회 속도 향상
- 테이블 전체를 풀스캔(Full Table Scan)하지 않고, 원하는 데이터를 빠르게 찾을 수 있음
- 인덱스를 사용하면 랜덤 I/O가 발생하지만, 필요한 데이터만 읽기 때문에 전체 테이블을 읽는 것보다 I/O 작업량이 줄어듬
단점
- 추가적인 저장 공간 필요
- 데이터 삽입, 수정, 삭제 시 추가적인 연산 필요 (정렬을 유지해야하기 때문에 추가 연산)
- 삽입: 새로운 데이터에 대한 인덱스를 추가
- 삭제: 삭제하는 데이터의 인덱스를 사용하지 않는다는 작업 수행
- 수정: 기존 인덱스를 사용하지 않음 처리하고 갱신된 데이터의 인덱스 추가
인덱스 설정 기준
WHERE, JOIN, GROUB BY, ORDER BY 조건에 자주 사용되는 컬럼
- INSERT, UPDATE, DELETE 연산이 자주 발생하지 않는 컬럼
- 카디널리티가 높고 선택도가 낮은 컬럼
- 카디널리티가 높다는 것은 한 컬럼이 가지는 값의 중복도가 낮다는 것을 의미 (해당 컬럼 값이 다양함)
- 주민등록번호 (높음)
- 성별(M or W) (낮음)
- 선택도가 낮다는 것은 한 컬럼으로 조건을 검색했을 때, 결과 집합이 작다는 뜻
- 자주 조합되어 사용되는 컬럼들은
결합인덱스로 사용
- 첫 번째 컬럼부터 조건에 사용되어야 인덱스를 제대로 활용 가능
- 규모가 작지 않는 테이블
카디널리티가 낮은 컬럼에 인덱스를 걸면, 더 많은 데이터를 랜덤 I/O를 해야하기 때문에, 오히려 테이블 풀스캔(순차 I/O)으로 데이터를 찾는것이 더 좋을 수 있음
인덱스 구조
- 일반적으로 Hash, B-Tree, B+Tree 등으로 사용해 구현됨
1. Hash
- Key-Value 형태의 자료구조
- 주로
= 조건의 검색에 최적화되어 있으며 O(1) 시간 복잡도를 가짐
- 실제 키 값과와 관계없이 해시 값을 저장하기 때문에 인덱스 크기가 작음
- 정렬되어 있지 않기때문에 범위 검색에는 적합하지 않음
- RDBMS에서는 잘 사용하지 않고 제한적으로 사용됨
2. B-Tree
- 이진 탐색 트리는 탐색 시간 복잡도가 O(logN)를 가지지만, 트리가 편향될 경우 최악의 시간복잡도는 O(N)를 가짐
- B-Tree는 Balcened Tree(균형 트리)를 기반으로, 삽입과 삭제 작업 후에도 트리가 자동으로 균형을 유지하여 시간 복잡도가 O(logN) 으로 보장
- 모든 리프 노드는 같은 레벨에 위치하여 트리가 항상 균형을 유지
- 자식 노드를 2개이상 가질 수 있음
- 키는 정렬된 상태로 저장되어, 범위 검색에 효율적

한계
- 데이터를 내부 노드(루트 노드 및 리프 노드가 아닌 노드)와 리프 노드(자식 노드가 없는 노드)에 분산 저장하므로, 특정 키를 검색하거나 탐색 시 디스크 접근이 더 자주 발생
- 데이터를 내부 노드와 리프 노드에 분산 저장하므로, 범위 검색이나 순차적 데이터 접근 시 여러 노드를 반복적으로 탐색해야 하여 비효율적
3. B+Tree
- B-Tree의 한계를 개선하고 확장한 자료구조로, 특히 범위 검색과 순차적 데이터 접근에서 효율적
- 모든 데이터는 리프 노드에서만 저장되고, 내부노드는 탐색을 위한 키와 포인터만 포함, 이때 키가 중복 될 수 있음
- 리프 노드 간 연결 리스트 형태로 연결되어 순차적 데이터 접근이 효율적
- 리프노드를 제외하고 데이터를 저장하지 않기 때문에 메모리를 더 확보할 수 있기에 하나의 노드에 더 많은 포인터를 가질 수 있음
=> 이로 인해 트리의 높이가 더 낮아지므로 검색 속도를 높임
- 검색은 B-Tree와 동일하지만, 삽입 및 삭제는 항상 리프노드에서 이루어짐
- B-Tree의 경우 최상의 경우 특정 키를 루트노드에서 찾을 수 있지만, B+Tree는 특정 키에 접근하기 위해 반드시 리프노드까지 가야하는 단점이 있음

B Tree 계열을 DB 인덱스로 왜 사용할까?
- DB는 기본적으로 Secondary storage(SSD or HDD)에 저장됨
Secondary storage(SSD or HDD) 특징
- 데이터를 처리하는 속도가 가장 느림
- 속도 비교: RAM < SSD < HDD
- DB 조회 시 Secondary Storage 접근 최소화가 성능에 매우 중요
- 데이터를 저장하는 용량이 가장 큼
- block 단위로 데이터를 읽고 씀
- block: file system이 데이터를 읽고 쓰는 논리적인 단위 (4KB, 8Kb, 16KB...)
- 이는 불필요한 데이터까지 읽어올 가능성이 있음
B-Tree 인덱스의 장점 (Self-balancing Binary Search Tree와 비교)
- 여러 개의 자식 노드를 가질 수 있음
- 트리의 깊이가 낮아져 Secondary Storage 접근이 줄어듦
- 하나의 노드에 여러 개의 데이터를 저장할 수 있음
- 블록 단위로 데이터를 읽고 쓰는 Secondary Storage 특성에 최적화
- 저장 공간을 효율적으로 활용하고, 한 번의 I/O로 더 많은 데이터를 처리 가능
인덱스 종류
1. 클러스터 인덱스(Clustered Index)
- 테이블 자체가 정렬된 인덱스 구조로 저장됨
- 주로 PK(Primary Key)로 생성되며, 하나의 테이블에는 하나의 클러스터 인덱스만 생성 가능
- 데이터가 정렬되어 저장되므로, 범위 검색, 순차적 데이터 접근에 유용
- 데이터 삽입/수정/삭제 시 물리적 정렬을 유지해야 하므로 오버헤드가 발생
예시: 사용자 테이블에서 ID 컬럼을 기준으로 데이터를 정렬
2. 논 클러스터 인덱스(Non Clustered Index)
- 테이블의 물리적 데이터 순서와 독립적으로 생성되는 인덱스
- 인덱스에는 키 값과 해당 데이터의 포인터가 저장되며, 실제 데이터는 테이블에 별도로 저장됨
- 하나의 테이블에 여러개 논 클러스터 인덱스 생성 가능
- 실제 데이터 페이지는 정렬되지 않으므로, 삽입, 삭제, 수정연산이 빠르나 검색연산이 클러스터 인덱스보다 느림
예시: 사용자 테이블의 email 컬럼에 대해 검색 성능을 최적화하는 논 클러스터 인덱스
3. 유니크 인덱스(Unique Index)
- 인덱스에 저장된 값이 중복되지 않도록 보장하는 인덱스
- Primary Key와 Unique Key 제약 조건에 의해 자동으로 생성
- 삽입, 수정 시 중복 확인을 위한 오버헤드 발생
예시: email 컬럼에 유니크 인덱스를 생성하여 중복 이메일 주소 삽입 방지
4. 결합 인덱스(Composite Index)
- 여러 컬럼을 결합하여 하나의 인덱스를 생성
- 인덱스의 컬럼 순서가 쿼리의 조건 순서와 맞아야 제대로 활용 가능
예시: (A, B)로 구성된 결합 인덱스는 WHERE A = 1 AND B = 2 조건에서는 제대로 활용되지만, WHERE B = 2 조건에서는 인덱스를 충분히 활용하지 못할 수 있음. 단, WHERE A = 1 조건만 사용하는 경우에는 A 컬럼에 대해서는 인덱스를 활용할 수 있음
5. 비트맵 인덱스 (Bitmap Index)
- 컬럼의 값을 비트맵으로 변환하여 저장
- 카디널리티가 낮은 데이터(값의 종류가 적음)에 적합
- 논리 연산(AND, OR)에 효율
- 저장 공간이 적고 읽기 작업에 효율
- 삽입/수정/삭제 시 갱신 비용이 큼
예시: 성별(M/F) 또는 상태코드(0/1)와 같은 값의 종류가 적은 데이터에 적용
6. 전문 검색 인덱스 (Full-Text Index)
- 텍스트 데이터 검색에 최적화된 인덱스
- 역색인(Inverted Index) 방식
- 각 단어를 키로, 해당 단어가 포함된 문서의 목록을 값으로 저장
- 자연어 검색, 부울 검색(AND, OR, NOT) 등 고급 검색 기능 지원
- 텍스트 데이터에서 키워드 검색, 유사 검색, 단어 간 거리 조건을 효율적으로 처리
예시: 제품 설명에서 키워드 기반 검색
7. 커버링 인덱스 (Covering Index)
- 쿼리에서 필요한 모든 데이터를 인덱스만으로 처리할 수 있는 인덱스
- 테이블의 데이터 페이지에 접근하지 않고도 인덱스만으로 결과를 반환할 수있어, 디스크 I/O를 최소화