사실 데이터베이스는 논리적인 표가 아니라 물리적인 파일입니다.
성능 최적화의 본질은 디스크 I/O 최소화에 달렸습니다.
내부는 데이터를 Page단위로 묶고, 효율적인 길찾기를 위해 인덱스와 heap이라는 구조를 사용합니다.
DB는 디스크와 대화할 때 '바이트'가 아닌 '페이지'라는 덩어리로 대화합니다.
크기: Postgres는 8KB, MySQL은 16KB가 기본입니다.
내부 구조: 페이지 안에는 헤더, 데이터(튜플, 헹), 데이터 위치를 알려주는 슬롯
참고: 페이지라도 부르고 블록이라고 부릅니다. 보통 1개의 DB 페이지는 여러 개의 OS 블록으로 구성
작은 행 하나를 수행해도 DB는 페이지 전체를 새로 씁니다. 컬럼이 많은 테이블은 한 페이지에 담기는 행의 수를 줄여 I/O 효율을 떨어뜨립니다.
인덱스가 없는 테이블을 힙이라고 부릅니다.
특징: 데이터가 들어온 순서대로(혹은 빈 공간에) 무작위로 쌓입니다.
Full Table Scan: 특정 데이터를 찾으려면 첫 페이지부터 마지막 페이지까지 전부 읽어야 합니다.
모든 데이터 행은 고유한 물리적 주소를 가집니다.
구성: 파일 번호 + 페이지 번호 + 페이지 내 오프셋으로 구성
인덱스의 역할: 인덱스는 우리가 찾는 값(예: 이름)을 Row ID로 변환해주는 매핑 테이블입니다.
인덱스에서 주소를 찾으면 힙으로 바로 점프 가능
대부분의 DB는 B-Tree 구조로 인덱스를 관리합니다.
구조: Root -> Branch -> Leaf 노드로 이어집니다.
탐색 효율: Leaf 노드에 도달하면 우리가 찾는 값과 해당 값의 Row ID를 얻습니다. 이 과정은 매우 빨라서 수억 건의 데이터 중에서도 단 몇 번의 I/O만으로 위치를 찾습니다.
클러스터드 (MySQL 등): 기본 키(PK) 순서대로 데이터 자체가 정렬되어 저장됩니다. 인덱스 자체가 곧 데이터입니다. 범위 검색(Range Scan)에 압도적으로 유리합니다.
세컨더리 (Postgres 등):
SELECT *를 할 때 보조 인덱스를 타면 인덱스 검색 후 테이블로 다시 점프해야 하므로 성능 손해가 발생합니다.| 플랫폼 | 특징 |
|---|---|
| MySQL | PK가 클러스터형 인덱스로 강제됨. 순차적인 PK(Auto-increment)가 유리함. |
| Postgres | 모든 인덱스는 보조 인덱스. 클러스터링을 하려면 CLUSTER 명령을 명시적으로 수행해야 함. |
| Oracle | IOT(Index Organized Table)를 선택할 수 있는 옵션을 제공함. |
인덱스는 정렬된 상태를 선호합니다. UUID같은 경우 랜덤이라 페이지가 쪼개지고(Page Split) 데이터 배치가 엉망이 되어 쓰기 성능이 급락
가급적이면 증가하는 숫자 타입이나 타임스탬프 기반 ID를 써서 인덱스 끝에만 데이터가 붙게 하는 것이 효율적
인덱스는 꽁짜가 아닙니다.
저장 공간: 인덱스도 결국 별도의 파일이라 디스크를 차지
수정 비용: INSERT, UPDATE, DELETE가 발생할 때마다 해당 테이블에 걸린 모든 인덱스를 다시 계산해서 업데이트해야 합니다. 인덱스가 10개 걸린 테이블은 쓰기 작업 시 10번의 추가 작업이 일어나는 셈입니다.
쓰기 부하(Write Amplification): 인덱스가 많으면 조회는 빠르지만 삽입/수정 시 모든 인덱스 페이지를 업데이트해야 하는 비용이 발생합니다.
행 기반: 특정 ID를 가진 사람의 정보를 가져오거나, insert, update, delete같은 작업에 유리
열 기반: 집계 함수사용 시 유리, 압축률이 좋다. 단, select * 하면 조립해야해서 최악의 성능(I/O 쓰레싱 발생)
Postgres나 Oracle같은 전통적인 행 기반 DB에도 특정 테이블만 열 기반으로 저장하거나 인덱싱하는 기능 제공
동작 원리: 특정 행을 요청하면 DB는 해당 행 포함한 페이지 찾아 디스크에 읽어 버퍼 풀(공유 메모리)에 올립니다.
효율성: 한 번 메모리에 올라온 페이지에 있는 다른 행들은 I/O없이 접근 가능
지연 쓰기: 업데이트 시 메모리 상 페이지만 먼저 수정하고, 변경 로그(WAL)만 남긴 뒤 나중에 디스크로 한꺼번에 플러시하여 성능을 높입니다.
무조건 크거나 작다고 좋은 것이 아닙니다.
소형 페이지 (8KB): 개별 읽기/쓰기가 빠르고 경합이 작지만, 헤더 메타데이터가 차지하는 비율이 높아집니다.
대형 페이지 (32KB): 한 번에 많은 데이터 가져올 수 있어 분석에 유리하지만, 작은 데이터 하나만 바꿔도 전체를 다뤄야 하므로 쓰기 비용이 커집니다.