[DATABASE] MySQL 의 InnoDB 의 구조

이영재·2025년 5월 21일
0

DB 정복 대작전

목록 보기
4/5

1. InnoDB 소개

MySQL 8.0부터는 InnoDB가 기본 스토리지 엔진으로 채택되었으며 트랜잭션, 외래 키, MVCC, 충돌 제어 같은 고급 기능을 지원하는 강력한 저장 엔진이다.

단순히 데이터를 디스크에 저장하는 데 그치지 않고 메모리 관리, 로그 처리, 쓰기 최적화까지 수행하는 복잡한 내부 구조를 갖고 있다.

1.1 InnoDB의 장점

  • PK 에 의한 클러스터링
  • 트랜잭션 지원
  • 데이터 무결성을 유지하기 위해 제약 조건 지원 (FOREIGN KEY 사용 가능)
  • 또한 DB를 효율적으로 사용하기 위한 여러가지 기능을 지원한다.

🤔 원래 이런거 지원해야하는거 아니야???

그럼 InnoDB를 말고 다른 DB 스토리지 엔진에 대해서 알아보자.

MyISAM은 MySQL 5.1 이전 버전에서 기본(Default) 엔진이었다.
그 당시에는 트랜잭션이나 외래 키 같은 기능보다는 단순하고 빠른 읽기 성능이 중요했기 때문에 MyISAM이 많이 쓰였다.

그래서 MyISAM은 트랜잭션, 외래 키(FK), 동시성 처리와 같은 기능은 없었다. 이를 개선한 것이 InnoDB 인데 처음엔 느리다는 이미지가 있었지만, 버전이 올라가면서 성능 최적화 + 기능성 + 안정성 모두 압도적이 되어서 MyISAM을 대체하게 되었다.

결론 : 실무에서는 지금 MyISAM을 사용하는 경우는 거의 없고 일부 특수 용도(정적 읽기 전용, 검색 인덱스 캐시 등) 외에는 InnoDB가 무조건 권장된다.
(MyISAM 에서도 정규화 (무결성 보장 X), 인덱스를 사용할 수는 있음)

2. InnoDB 아키텍처

구조는 크게 메모리 구조(In-Memory Structures)디스크 구조(On-Disk Structures)로 나뉘고 둘은 운영체제 캐시(=I/O 인터페이스)를 통해 상호작용한다.

2.1 메모리 구조(In-Memory Structures)

2.1.1 버퍼 풀

버퍼 풀(Buffer Pool)은 InnoDB에서 테이블과 인덱스 데이터를 캐시하는 주 메모리 공간이다.
디스크 접근을 최소화하고, 자주 조회되는 데이터를 메모리에서 직접 처리할 수 있도록 하여 전체 성능을 크게 향상시킨다.

  • 대규모 서버에서는 전체 메모리의 최대 80%까지 할당되기도 한다.
  • 데이터는 페이지 단위(기본 16KB)로 관리되며, 페이지는 연결 리스트(LRU 방식)로 유지된다.

Buffer Pool 동작 흐름

버퍼 풀은 변형된 LRU 알고리즘을 사용하여 페이지를 관리한다.

  • 전체 리스트는 New SublistOld Sublist 하위 목록으로 나뉜다.
  • 새로 읽어온 페이지는 중간 지점(=old 하위 목록의 앞)에 삽입된다.
  • 실제 사용자 쿼리로 접근된 페이지는 young 목록으로 승격된다.
  • 한 번도 접근되지 않은 페이지는 old 목록에서 제거 대상이 된다.

2.1.2 Change Buffer

Change Buffer는 InnoDB가 보조 인덱스(Secondary Index)에 대한 변경 작업 (INSERT, UPDATE, DELETE)을 수행할 때
해당 인덱스 페이지가 Buffer Pool(메모리)에 없다면 즉시 디스크를 읽지 않고 변경 내용을 임시 저장해두는 특수 캐시 구조다.

  • 대상 페이지가 버퍼 풀에 없는 경우에만 사용
  • 나중에 해당 페이지가 버퍼 풀에 로드될 때 병합(Merge)된다.

🤔 그럼 왜 굳이 메모리에 임시로 저장해둘까?

보조 인덱스는 정렬되지 않은 상태에서 삽입/삭제가 발생하고 이로 인해 디스크의 여러 위치를 랜덤하게 접근해야 한다.
하지만 디스크는 랜덤 I/O에 매우 취약하고 느리기 때문에 변경이 발생할 때마다 매번 디스크를 읽어오는 건 성능 낭비가 크다.

그래서 InnoDB는 보조 인덱스 페이지가 메모리에 없을 경우 변경 내용을 바로 디스크에 적용하지 않고 Change Buffer에 임시로 기록해둔다.
→ 해당 Buffer Pool에 로드될 때 미뤄뒀던 변경 내용을 한꺼번에 병합하면서 불필요한 디스크 접근을 줄이고 전체 성능을 높이는 구조

Change Buffer 동작 흐름

보조 인덱스에 대한 변경 작업(INSERT, UPDATE, DELETE)이 발생했을 때 해당 인덱스 페이지가 버퍼 풀에 존재하지 않고

  • 변경 내용을 변경 버퍼(Change Buffer)에 먼저 저장해둔다.

이후 해당 보조 인덱스 페이지가 다른 쿼리나 작업에 의해 버퍼 풀에 로딩되면 변경 버퍼의 내용이 해당 페이지에 병합(Merge) 되고

  • 최종적으로 디스크에 플러시(Flush)되어 반영된다.

2.1.3 Adaptive Hash Index

적응형 해시 인덱스(AHI)는 InnoDB가 자주 조회되는 B+Tree 인덱스 페이지의 검색 패턴을 감지하고 자동으로 해시 인덱스를 생성함으로써 인덱스 검색 속도를 비약적으로 향상시키는 기능이다.

적응형 해시 인덱스(AHI)가 왜 빠른가?

  • 이미 찾은 주소를 해시 값으로 테이블로 저장해놓고 다음 요청에 이 테이블을 활용하는 것!

Adaptive Hash Index 동작 과정

SELECT * FROM user WHERE email = 'jay@domain.com';

  1. 위 쿼리가 처음 실행될 때
    • InnoDB는 기존 B+Tree 인덱스를 타고 "jay@domain.com" 키를 찾는다.
    • 이 과정에서 B+Tree의 리프 노드에 있는 인덱스 레코드 주소를 메모리에서 확인한다
    • 그 주소는 Buffer Pool의 특정 위치
  2. AHI는 이 정보들을 자동으로 저장한다
    • InnoDB 가 자주 조회되는 키 판단
    • Hash(key) → 인덱스 위치로 변환되는 구조가 만들어진다.
  3. 다음 쿼리에서 InnoDB 의 동작
    • 이제 InnoDB는 B+Tree를 타지 않고
      • 'jay@domain.com' → 해시 계산
      • 해시 테이블에서 매핑된 주소를 즉시 조회
      • 그 위치로 직접 점프해서 레코드 조회

2.1.4 Log Buffer

로그 버퍼(Log Buffer)는 InnoDB에서 트랜잭션 중 변경된 데이터를 디스크에 기록하기 전까지 임시 저장하는 메모리 공간이다.
이 공간에는 Redo Log에 기록될 내용이 담기며, 디스크에 기록되기 전까지는 로그 버퍼에 머문다.

🤔 아까 Change Buffer 랑 비슷한거 아니야?

  • Log Buffer는 모든 변경 이력을 기록해 복구 가능하게 만드는 트랜잭션 로그 공간
  • Change Buffer는 보조 인덱스 페이지가 없을 때 변경 내용을 임시 저장해 불필요한 디스크 읽기를 줄이는 캐시

왜 필요할까?

  • 트랜잭션마다 디스크에 바로 로그를 기록하면 I/O 오버헤드가 크기 때문
  • 로그 버퍼에 먼저 저장해두고, 일정 시점 또는 커밋 시점에만 디스크(Redo Log)로 플러시한다. → 디스크 접근 최소화 + 성능 향상

⚠️ 주의 사항
Log Buffer는 임시 저장소일 뿐, 복구를 보장하지 않는다.
진짜 복구의 근거는 Redo Log 파일이기 때문에 커밋 시점에 Log Buffer → Redo Log로 얼마나 빨리 플러시하느냐가 핵심이다.

2.2 디스크 구조(On-Disk Structures)

InnoDB는 데이터를 디스크에 저장할 때, 논리적 구조와 물리적 구조로 나누어 저장 구조를 설계한다.

논리적 구조: 데이터베이스 입장에서 보이는 구조 (테이블, 인덱스 등)
물리적 구조: 실제 디스크 파일과 저장 단위 (tablespace, 로그 등)


2.2.1 Tables (논리적 구조)

Table(테이블)은 데이터베이스에서 데이터를 행(Row)과 열(Column)로 저장하는 기본 단위이다.
하지만 InnoDB에서는 row가 단순히 저장되는 것이 아니라, 클러스터형 인덱스(Primary Key B+Tree)의 구조 안에 함께 저장된다.

  • 즉, 기본 키를 기준으로 데이터가 정렬된 상태로 디스크에 저장됨
  • 리프 노드에는 row 전체 값이 포함되어 있음
  • 테이블 = 클러스터 인덱스 = 디스크에 저장되는 실제 구조

기본 키로 조회하거나 범위 검색할 때 매우 빠름


2.2.2 Indexes (논리적 구조)

인덱스는 데이터를 빠르게 찾기 위한 자료구조이며, InnoDB는 B+Tree 구조의 인덱스를 사용한다.

  • Primary Key: 클러스터 인덱스, row 자체를 포함
  • Secondary Index (보조 인덱스): 별도의 구조로 존재하며,
    → 해당 인덱스 키와 함께 기본 키(PK)도 저장됨
    → 실제 row를 가져오기 위해 한 번 더 클러스터 인덱스로 Jump하는 방식

조회 성능 향상은 크지만, 보조 인덱스만으로는 row 전체를 알 수 없음


2.2.3 Tablespaces (물리적 구조)

테이블과 인덱스 데이터를 실제로 디스크에 저장하는 물리적 공간

  • InnoDB는 데이터를 Tablespace라는 단위로 파일에 저장함

  • 대표적인 구조:

    • ibdata1: 공유 테이블스페이스 (구버전)
    • .ibd: 각 테이블별 독립 저장 파일 (파일-퍼-테이블 모드)

데이터가 실제로 어느 파일에 저장되는지 이해하는 핵심


2.2.4 Doublewrite Buffer (물리적 구조)

InnoDB는 디스크 쓰기 중 장애가 발생할 경우를 대비해
데이터를 먼저 임시 버퍼 영역에 두 번 기록하는 방식으로 디스크 손상을 방지한다.

  • 페이지를 Doublewrite Buffer에 먼저 기록한 후 → 최종 테이블 공간에 기록
  • 디스크가 불안정한 환경에서도 데이터 손상 가능성 최소화

안정성 보장을 위한 쓰기 보호 메커니즘


2.2.5 Redo Log (물리적 구조)

트랜잭션의 변경 이력(무엇을 변경했는지)을 기록하는 로그 파일

  • Log Buffer에 먼저 저장되고,
    → 커밋 또는 주기적으로 Redo Log 파일(ib_logfile0, 등) 에 기록됨
  • 시스템 크래시 시 → 이 로그를 재실행하여 복구

WAL(Write-Ahead Logging)의 핵심 요소
데이터의 영속성과 복구 가능성 확보


2.2.6 Undo Log (물리적 구조, 논리적 개념 포함)

트랜잭션의 이전 값을 저장하는 로그
롤백, 그리고 MVCC(다중 버전 동시성 제어) 구현의 핵심

  • DELETEUPDATE 전에 이전 값을 Undo Log에 저장
  • 트랜잭션 롤백 시 → Undo Log를 통해 데이터 원상복구 가능
  • 트랜잭션 격리 수준(REPEATABLE READ)에서도 → Undo Log를 기반으로 과거 스냅샷 제공

트랜잭션의 정합성, MVCC의 기반이 되는 구조

3. 마무리

지금까지 InnoDB가 데이터를 디스크에 저장할 때 사용하는 구조를 살펴봤다.

단순히 데이터를 저장하고 조회하는 것처럼 보이지만, 내부적으로는 정렬된 인덱스 구조, 로그 기록, 복구를 위한 이중 버퍼링 등 다양한 최적화와 안정성 장치가 작동하고 있는 것이다.

앞으로의 인덱스 튜닝, 트랜잭션 처리, 성능 최적화에서 이 구조들이 어떤 식으로 영향을 주는지 하나씩 연결해보자.

0개의 댓글