Theme:

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에서 가장 중요한 메모리 영역입니다. 테이블 데이터와 인덱스 페이지를 메모리에 캐시합니다.

기본 동작

PLAINTEXT
읽기 요청 → Buffer Pool에 페이지 있음? → 있으면 메모리에서 반환 (캐시 히트)
                                      → 없으면 디스크에서 읽어와서 Buffer Pool에 적재

LRU(Least Recently Used) 알고리즘

Buffer Pool은 단순한 LRU가 아닌 변형된 LRU 리스트를 사용합니다.

PLAINTEXT
┌──────────────────────────────────────┐
│  Young 영역 (5/8)  │  Old 영역 (3/8)  │
│  (자주 접근하는      │  (새로 읽힌       │
│   핫 페이지)         │   페이지)         │
└──────────────────────────────────────┘
  • 새로 읽힌 페이지는 Old 영역의 head에 삽입됩니다
  • Old 영역에서 다시 접근되면 Young 영역으로 승격됩니다
  • 풀 테이블 스캔 시 한 번만 읽는 페이지가 핫 페이지를 밀어내는 것을 방지합니다

주요 설정

SQL
-- 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) 계산:

PLAINTEXT
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가 발생합니다.

PLAINTEXT
INSERT 실행 → 세컨더리 인덱스 페이지가 Buffer Pool에 없음
           → Change Buffer에 변경 사항 기록 (디스크 I/O 없음)
           → 나중에 해당 페이지가 읽힐 때 머지(Merge)

적용 조건

  • 세컨더리 인덱스에만 적용됩니다 (Primary Key는 해당 없음)
  • 유니크 인덱스에는 적용되지 않습니다 (유니크 체크를 위해 페이지를 읽어야 하므로)
  • INSERT, UPDATE, DELETE 모두 버퍼링 가능
SQL
-- Change Buffer 사용량 확인
SHOW STATUS LIKE 'Innodb_ibuf%';

-- Change Buffer 사용 범위 설정
-- innodb_change_buffering = all (기본값: inserts, deletes, purges 모두)

Adaptive Hash Index (AHI)

InnoDB는 기본적으로 B+Tree 인덱스를 사용하지만, 자주 접근하는 페이지에 대해 자동으로 해시 인덱스를 생성합니다.

PLAINTEXT
B+Tree 검색: O(log n) — 루트 → 중간 노드 → 리프 노드
해시 검색:   O(1)     — 해시 함수 → 바로 페이지 접근
  • InnoDB가 자동으로 판단하여 생성합니다 (DBA가 직접 생성할 수 없음)
  • 특정 패턴의 쿼리가 반복적으로 같은 페이지에 접근하면 활성화됩니다
  • 워크로드에 따라 오히려 성능을 저하시킬 수 있습니다
SQL
-- AHI 상태 확인
SHOW ENGINE INNODB STATUS\G
-- "Hash table size" 섹션에서 확인

-- AHI 비활성화 (필요 시)
SET GLOBAL innodb_adaptive_hash_index = OFF;

Log Buffer

Log Buffer는 Redo Log에 기록할 내용을 임시로 보관하는 메모리 영역입니다.

PLAINTEXT
트랜잭션 변경 → Log Buffer에 기록 → 주기적으로 Redo Log 파일에 플러시
SQL
-- 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초마다 동기화높음중간
01초마다 디스크 동기화중간가장 빠름

테이블스페이스

InnoDB의 데이터는 테이블스페이스 단위로 디스크에 저장됩니다.

시스템 테이블스페이스

PLAINTEXT
ibdata1 — 시스템 테이블스페이스 파일
├── Data Dictionary (메타데이터)
├── Doublewrite Buffer
├── Change Buffer
└── Undo Logs (5.6 이전 기본)

File-Per-Table 테이블스페이스

MySQL 5.6.6부터 기본값으로, 테이블마다 별도의 .ibd 파일을 생성합니다.

PLAINTEXT
my_database/
├── users.ibd       ← users 테이블 데이터 + 인덱스
├── orders.ibd      ← orders 테이블 데이터 + 인덱스
└── products.ibd    ← products 테이블 데이터 + 인덱스

장점:

  • 테이블 단위로 백업/복원이 용이합니다
  • TRUNCATE TABLE이 즉시 디스크 공간을 반환합니다
  • 테이블별 용량 확인이 쉽습니다

General 테이블스페이스

SQL
-- 여러 테이블을 하나의 테이블스페이스에 저장
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) 가 발생할 수 있습니다.

PLAINTEXT
데이터 페이지 → Doublewrite Buffer (순차 쓰기) → 실제 테이블스페이스 (랜덤 쓰기)
  • 크래시 복구 시 Doublewrite Buffer에서 완전한 페이지를 찾아 복구합니다
  • 쓰기 I/O가 2배가 되지만, 순차 쓰기이므로 실제 오버헤드는 5~10% 수준입니다
  • MySQL 8.0.20부터 Doublewrite Buffer가 별도 파일로 분리되었습니다

InnoDB 전체 아키텍처 요약

PLAINTEXT
┌─────────────── 메모리 영역 ───────────────┐
│  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                 │
└───────────────────────────────────────────┘

모니터링 명령어

SQL
-- 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 성능에 가장 큰 영향을 미치는 설정입니다
댓글 로딩 중...