Database/Mysql

[Real MySQL] 4-2. 아키텍쳐: InnoDB 스토리지 엔진 아키텍쳐

noahkim_ 2023. 11. 24. 00:57

백은빈, 이성욱 님의 "Real MySQL" 책을 정리한 포스팅 입니다.

 



1. InnoDB 스토리지 엔진

기능

기능 항목 설명
레코드 기반 잠금 행(레코드) 단위 잠금
→ 충돌 최소화, 정밀한 동시성 제어 가능
ACID 트랜잭션 지원 원자성, 일관성, 격리성, 지속성을 보장
→ 신뢰성 높은 데이터 처리 가능
MVCC (다중 버전 동시성) 읽기/쓰기 작업 간 충돌 없이 처리 가능
→ 비차단 읽기(Non-locking read) 지원
크래시 복구 기능 로그 기반 자동 복구
→ 장애 발생 시에도 데이터 복구 가능
외래 키 제약 조건 데이터 참조 무결성 보장
→ 복잡한 관계형 데이터 설계 가능

 

장점

장점 항목 설명
높은 동시성 처리 여러 트랜잭션이 동시에 다른 레코드에 접근 가능
→ 병렬 처리 효율적
우수한 성능 필요한 레코드에만 잠금 적용
→ 불필요한 락 대기 줄고, 성능 향상
데이터 정합성 보장 트랜잭션과 복구 메커니즘을 통해 일관성 유지
데드락 감지 및 회피 내부적으로 데드락 감지 가능
→ 문제 발생 시 자동 롤백 등으로 대응
신뢰성 높은 운영 가능 장애 복구 및 무결성 제어 기능으로 안정적인 운영 환경 제공

 

2. 클러스터링

클러스터형 인덱스 구조

  • 테이블 자체가 프라이머리 키 인덱스와 결합되어 저장되는 구조
  • InnoDB에서는 프라이머리 키가 클러스터형 인덱스 역할을 하며, 해당 인덱스 안에 테이블의 모든 컬럼 데이터가 함께 저장됨.
항목 자세한 설명
테이블 = 인덱스
별도의 테이블 파일에 데이터를 따로 저장하지 않음
프라이머리 키 인덱스의 B+ Tree 구조의 리프(Leaf) 노드에 모든 데이터가 포함됨.
정렬 저장
이 구조 때문에 데이터가 프라이머리 키 순서대로 물리적으로 정렬되어 저장됨
→ 범위 조회에 특히 효율적.

 

보조 인덱스 구조

  • 프라이머리 키가 아닌 다른 컬럼에 대해 생성한 인덱스 (예: email, username 등).
항목 자세한 설명
구조적 특징 인덱스 컬럼 값 + 해당 레코드의 프라이머리 키 값만을 저장함
데이터 접근 방식
1. 보조 인덱스를 통해 일치하는 프라이머리 키를 찾음
2. 해당 PK로 다시 클러스터형 인덱스(B+ Tree)를 탐색
 실제 레코드를 조회함.
⚠️ 주의점
이중 접근이 필요하므로, 보조 인덱스를 통한 조회는 PK 조회보다 느릴 수 있음. (특히 선택도가 낮은 경우 더더욱)

 

프라이머리 키의 가중치

항목 자세한 설명
우선적 인덱스 설정
InnoDB는 프라이머리 키가 클러스터형 인덱스이기 때문에, 데이터 저장 구조의 핵심이 됨.
따라서 시스템 차원에서도 가장 우선적으로 고려되는 인덱스임.
인덱스 설계 영향
프라이머리 키는 모든 보조 인덱스에도 자동으로 포함되므로, 너무 길거나 자주 변경되는 컬럼은 PK로 부적절함.
효율성 측면
프라이머리 키가 짧고 고유하며 자주 정렬, 범위 검색에 쓰인다면 성능상 매우 효율적임.
✅ 설계 팁
프라이머리 키는 가급적 단순하고 불변이며 정수 기반(auto_increment 등)으로 설계하는 것이 좋음.

 

3. 외래 키 지원

제약

항목 설명
슈퍼타입-서브타입 제약
서브타입 테이블은 슈퍼타입 테이블의 **기본 키(PK)**를 외래 키로 참조해야 함 (관계형 모델의 정석 구조)
참조 무결성 제약 자식 테이블의 외래 키 값은 부모 테이블의 PK 또는 UK 값과 일치해야만 함

 

외래 키 잠금 전파

  • 외래 키 제약이 활성화 되면 부모 테이블과 자식 테이블에 대해 잠금을 수행하게 됨
  • 자식 테이블에서 데이터를 업데이트 하거나 삭제할 때 부모 테이블도 잠김
  • 여러 테이블이 잠금 상태에 놓이게 되므로 데드락 발생 가능성이 발생함

 

시스템 변수

  • foreign_key_checks: 참조 무결성 제약 원칙 활성화

 

4. MVCC (Multi Version Concurrency Control)

  • 레코드 레벨 수준의 동시성 제어 기법
  • 다중 버전 동시성 제어: 하나의 레코드에 대해 여러 버전을 관리

 

Non-Locking Read

  • 잠금 없이 읽기 가능
  • 다른 트랜잭션의 변경을 보지 않고, 트랜잭션 시작 시점의 일관된 데이터를 읽음
항목 설명
Undo Log 활용 트랜잭션 시작 시점 기준으로 가장 최신의 버전을 보여줌
버전 관리 수단 Undo Log를 활용하여 이전 버전을 저장함

 

Undo Log

항목 설명
Undo Log 생성 시점 트랜잭션이 데이터를 변경하기 전, 기존 값을 Undo Log에 기록
Undo Log 활용
읽기 트랜잭션은 Undo Log를 참고하여 변경 전 데이터(과거 버전)를 읽을 수 있음
Rollback 동작 Undo Log의 값을 이용해 변경 전 상태로 되돌림
Commit 이후 처리 커밋이 완료되면 해당 Undo Log는 필요 없어져 삭제됨

 

트랜잭션 처리 흐름

단계 동작 내용
UPDATE 변경된 값은 InnoDB Buffer Pool에 먼저 저장됨 (디스크에 즉시 반영되지 않음)
COMMIT Buffer Pool의 데이터를 디스크에 반영(Flush)하고, 해당 Undo Log는 삭제됨
ROLLBACK
Undo Log를 참조해 데이터를 기존 상태로 복원함 (커밋되지 않았기 때문에 디스크 반영 없음)

 

5. 자동 데드락 감지

  • InnoDB는 잠금 대기 상태를 그래프 형태로 관리하여, 데드락(순환 대기)이 발생하면 자동으로 감지하고 해결함
  • 별도 감지 스레드가 주기적으로 트랜잭션 상태를 분석함

 

데드락 감지 구조

항목 설명
잠금 대기 목록 어떤 트랜잭션이 어떤 자원을 기다리는지를 나타내는 구조
→ 그래프 형태로 구성됨
순환 대기 검사 대기 그래프 내에 루프(순환 구조)가 생겼는지 탐지
→ 순환 구조 = 데드락
그래프 잠금
분석 도중 잠금 대기 목록 자체에 락을 걸어, 다른 트랜잭션이 동시에 상태를 바꾸지 못하도록 함

 

해결 방식

항목 설명
감지 스레드의 역할 데드락 발생 시, 가장 비용이 낮은 트랜잭션 하나를 강제 롤백하여 데드락을 해소함
롤백 대상 기준 보통 Undo Log 크기가 작고 처리 비용이 적은 트랜잭션을 선택함
롤백 후 동작
롤백된 트랜잭션은 ERROR 1213 (40001): Deadlock found when trying to get lock 등의 에러를 받고 종료됨

 

시스템 변수

  • innodb_deadlock_detect: 데드락 감지 스레드 동작 여부
  • innodb_table_locks: 테이블 레벨의 잠금까지 감지하는지 여부
  • innodb_lock_wait_timeout: 데드락 상황에서 일정 시간이 지나면 에러나도록 타임아웃 시간 설정

 

6. 자동화된 장애 복구

  • MySQL 서버가 시작될 때, InnoDB는 자동으로 손상된 트랜잭션이나 데이터 페이지를 검사하고 복구 시도
항목 설명
복구 대상 상황
트랜잭션 충돌, 서버 강제 종료, 로그 손상, 미완료된 트랜잭션, 디스크에 일부만 기록된 페이지 등
복구 메커니즘
Undo 로그, Redo 로그를 통해 복구를 수행
 필요한 경우 자동으로 롤백 또는 재적용 처리
복구 실패 시 대처
innodb_force_recovery를 통해 복구 범위를 조정하거나 강제로 서버를 읽기 전용 모드로 부팅 가능

 

innodb_force_recovery 시스템 변수

  • 복구 모드를 설정하는 시스템 변수
  • 레벨이 높을수록 더 많은 보구 작업을 무시하고 서버를 강제로 시작할 수 있음
  • SELECT 이외의 쿼리는 수행할 수 없습니다.

 

복구 모드
레벨 설명
0 (기본값) 복구 비활성화. 일반적인 상태. 모든 로그를 사용해 복구 시도
1 손상된 데이터나 인덱스 페이지가 있어도 무시하고 서버 시작 (읽기 전용)
2 백그라운드 스레드 실행 금지 (체크포인트, 리두 로그 플러시 비활성화)
3 비정상 종료된 트랜잭션 롤백하지 않음 → 데이터 정합성은 낮아질 수 있음
4 인서트 버퍼 손상 무시하고 재시작 가능
5 언두 로그 무시 → 트랜잭션 상태 복원이 불가함 (데이터 무결성 위험)
6 리두 로그 손상 무시 → 가장 위험한 수준, 가능한 최후 수단으로만 사용

 

 

출처