InnoDB 아키텍처 — MySQL 기본 엔진의 내부 구조
MySQL에 데이터를 저장하면 디스크에 바로 쓰일까요, 아니면 어딘가에 먼저 보관되었다가 쓰일까요?
InnoDB는 MySQL 8.0의 기본 스토리지 엔진입니다. 트랜잭션, 행 수준 잠금, 크래시 복구를 모두 지원하며, 그 뒤에는 정교한 내부 아키텍처가 숨어 있습니다. 이 글에서는 InnoDB의 핵심 구성 요소를 하나씩 살펴보겠습니다.
개념 정의
InnoDB 아키텍처는 크게 메모리 영역과 디스크 영역으로 나뉩니다.
- 메모리 영역: Buffer Pool, Change Buffer, Adaptive Hash Index, Log Buffer
- 디스크 영역: 테이블스페이스, Redo Log, Undo Log, Doublewrite Buffer
모든 읽기/쓰기는 가능한 한 메모리에서 처리하고, 디스크 I/O를 최소화하는 것이 InnoDB 설계의 핵심 철학입니다.
Buffer Pool — InnoDB의 심장
Buffer Pool은 InnoDB에서 가장 중요한 메모리 영역입니다. 테이블 데이터와 인덱스 페이지를 메모리에 캐시합니다.
기본 동작
읽기 요청 → Buffer Pool에 페이지 있음? → 있으면 메모리에서 반환 (캐시 히트)
→ 없으면 디스크에서 읽어와서 Buffer Pool에 적재
LRU(Least Recently Used) 알고리즘
Buffer Pool은 단순한 LRU가 아닌 변형된 LRU 리스트를 사용합니다.
┌──────────────────────────────────────┐
│ Young 영역 (5/8) │ Old 영역 (3/8) │
│ (자주 접근하는 │ (새로 읽힌 │
│ 핫 페이지) │ 페이지) │
└──────────────────────────────────────┘
- 새로 읽힌 페이지는 Old 영역의 head에 삽입됩니다
- Old 영역에서 다시 접근되면 Young 영역으로 승격됩니다
- 풀 테이블 스캔 시 한 번만 읽는 페이지가 핫 페이지를 밀어내는 것을 방지합니다
주요 설정
-- Buffer Pool 크기 (전용 서버에서 전체 메모리의 50~80% 권장)
SET GLOBAL innodb_buffer_pool_size = 8589934592; -- 8GB
-- Buffer Pool 인스턴스 수 (멀티코어 환경에서 경합 감소)
-- innodb_buffer_pool_instances = 8 (my.cnf에서 설정)
-- Buffer Pool 적중률 확인
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';
Buffer Pool 적중률(Hit Rate) 계산:
Hit Rate = 1 - (Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) × 100
99% 이상이면 양호, 95% 미만이면 Buffer Pool 크기 증가를 고려해야 합니다.
Change Buffer
Change Buffer는 세컨더리 인덱스의 변경 사항을 버퍼링하는 메모리 영역입니다.
왜 필요한가
세컨더리 인덱스는 Primary Key와 달리 데이터 삽입 순서와 인덱스 순서가 다릅니다. 매 INSERT마다 인덱스 페이지를 디스크에서 읽어와야 한다면 랜덤 I/O가 발생합니다.
INSERT 실행 → 세컨더리 인덱스 페이지가 Buffer Pool에 없음
→ Change Buffer에 변경 사항 기록 (디스크 I/O 없음)
→ 나중에 해당 페이지가 읽힐 때 머지(Merge)
적용 조건
- 세컨더리 인덱스에만 적용됩니다 (Primary Key는 해당 없음)
- 유니크 인덱스에는 적용되지 않습니다 (유니크 체크를 위해 페이지를 읽어야 하므로)
- INSERT, UPDATE, DELETE 모두 버퍼링 가능
-- Change Buffer 사용량 확인
SHOW STATUS LIKE 'Innodb_ibuf%';
-- Change Buffer 사용 범위 설정
-- innodb_change_buffering = all (기본값: inserts, deletes, purges 모두)
Adaptive Hash Index (AHI)
InnoDB는 기본적으로 B+Tree 인덱스를 사용하지만, 자주 접근하는 페이지에 대해 자동으로 해시 인덱스를 생성합니다.
B+Tree 검색: O(log n) — 루트 → 중간 노드 → 리프 노드
해시 검색: O(1) — 해시 함수 → 바로 페이지 접근
- InnoDB가 자동으로 판단하여 생성합니다 (DBA가 직접 생성할 수 없음)
- 특정 패턴의 쿼리가 반복적으로 같은 페이지에 접근하면 활성화됩니다
- 워크로드에 따라 오히려 성능을 저하시킬 수 있습니다
-- AHI 상태 확인
SHOW ENGINE INNODB STATUS\G
-- "Hash table size" 섹션에서 확인
-- AHI 비활성화 (필요 시)
SET GLOBAL innodb_adaptive_hash_index = OFF;
Log Buffer
Log Buffer는 Redo Log에 기록할 내용을 임시로 보관하는 메모리 영역입니다.
트랜잭션 변경 → Log Buffer에 기록 → 주기적으로 Redo Log 파일에 플러시
-- Log Buffer 크기 (기본 16MB)
-- innodb_log_buffer_size = 16777216
-- 플러시 주기 설정
-- innodb_flush_log_at_trx_commit = 1 (커밋마다 플러시 — 가장 안전)
-- innodb_flush_log_at_trx_commit = 0 (1초마다 플러시 — 최대 1초 데이터 손실 가능)
-- innodb_flush_log_at_trx_commit = 2 (커밋마다 OS 버퍼에 쓰기, 1초마다 디스크 동기화)
innodb_flush_log_at_trx_commit 값에 따른 차이:
| 값 | 동작 | 안전성 | 성능 |
|---|---|---|---|
| 1 | 커밋마다 디스크 동기화 | 최고 | 가장 느림 |
| 2 | 커밋마다 OS 버퍼, 1초마다 동기화 | 높음 | 중간 |
| 0 | 1초마다 디스크 동기화 | 중간 | 가장 빠름 |
테이블스페이스
InnoDB의 데이터는 테이블스페이스 단위로 디스크에 저장됩니다.
시스템 테이블스페이스
ibdata1 — 시스템 테이블스페이스 파일
├── Data Dictionary (메타데이터)
├── Doublewrite Buffer
├── Change Buffer
└── Undo Logs (5.6 이전 기본)
File-Per-Table 테이블스페이스
MySQL 5.6.6부터 기본값으로, 테이블마다 별도의 .ibd 파일을 생성합니다.
my_database/
├── users.ibd ← users 테이블 데이터 + 인덱스
├── orders.ibd ← orders 테이블 데이터 + 인덱스
└── products.ibd ← products 테이블 데이터 + 인덱스
장점:
- 테이블 단위로 백업/복원이 용이합니다
TRUNCATE TABLE이 즉시 디스크 공간을 반환합니다- 테이블별 용량 확인이 쉽습니다
General 테이블스페이스
-- 여러 테이블을 하나의 테이블스페이스에 저장
CREATE TABLESPACE my_space
ADD DATAFILE 'my_space.ibd'
ENGINE = InnoDB;
CREATE TABLE t1 (...) TABLESPACE my_space;
Doublewrite Buffer
InnoDB 페이지 크기는 기본 16KB이지만, 대부분의 파일시스템은 4KB 단위로 쓰기합니다. 16KB 쓰기 도중 크래시가 발생하면 부분 쓰기(Partial Page Write) 가 발생할 수 있습니다.
데이터 페이지 → Doublewrite Buffer (순차 쓰기) → 실제 테이블스페이스 (랜덤 쓰기)
- 크래시 복구 시 Doublewrite Buffer에서 완전한 페이지를 찾아 복구합니다
- 쓰기 I/O가 2배가 되지만, 순차 쓰기이므로 실제 오버헤드는 5~10% 수준입니다
- MySQL 8.0.20부터 Doublewrite Buffer가 별도 파일로 분리되었습니다
InnoDB 전체 아키텍처 요약
┌─────────────── 메모리 영역 ───────────────┐
│ Buffer Pool │ Log Buffer │
│ (데이터 캐시) │ (Redo Log 버퍼) │
│ │ │
│ Change Buffer │ Adaptive Hash Index │
│ (인덱스 변경) │ (자동 해시 인덱스) │
├─────────────── 디스크 영역 ───────────────┤
│ System Tablespace (ibdata1) │
│ File-Per-Table Tablespace (.ibd) │
│ Redo Log Files (ib_logfile0, 1) │
│ Undo Tablespace │
│ Doublewrite Buffer Files │
└───────────────────────────────────────────┘
모니터링 명령어
-- InnoDB 전체 상태 확인 (가장 상세)
SHOW ENGINE INNODB STATUS\G
-- Buffer Pool 통계
SELECT * FROM information_schema.INNODB_BUFFER_POOL_STATS;
-- 현재 Buffer Pool에 있는 페이지 확인
SELECT TABLE_NAME, INDEX_NAME, NUMBER_RECORDS, DATA_SIZE
FROM information_schema.INNODB_BUFFER_PAGE
WHERE TABLE_NAME IS NOT NULL
LIMIT 20;
정리
- InnoDB는 Buffer Pool을 중심으로 디스크 I/O를 최소화하는 구조입니다
- Change Buffer는 세컨더리 인덱스의 랜덤 I/O를, Log Buffer는 Redo Log의 동기 쓰기를 줄여줍니다
- Adaptive Hash Index는 반복 접근 패턴에서 B+Tree보다 빠른 조회를 제공합니다
- Doublewrite Buffer는 부분 쓰기로 인한 데이터 손상을 방지합니다
innodb_buffer_pool_size는 InnoDB 성능에 가장 큰 영향을 미치는 설정입니다