"SQL을 쓰는 것과, DB가 내부에서 무슨 일을 하는지 아는 것은 다르다"
"인덱스를 걸었는데 쿼리가 느리고, 격리 수준을 설정했는데 Phantom Read가 발생하고, 데드락 로그를 보고도 원인을 모른다면 — DB를 블랙박스로 두고 있는 것이다"
InnoDB Buffer Pool의 Page 교체 원리부터 B-Tree 인덱스가 Binary Tree가 아닌 이유, MVCC가 Undo Log로 이전 버전을 보관하는 방식, Gap Lock이 Phantom Read를 막는 메커니즘까지
왜 이렇게 설계됐는가 라는 질문으로 MySQL InnoDB 내부를 끝까지 파헤칩니다
DB에 관한 자료는 넘쳐납니다. 하지만 대부분은 "어떻게 쓰나" 에서 멈춥니다.
| 일반 자료 | 이 레포 |
|---|---|
| "인덱스를 걸면 빠릅니다" | B-Tree가 Binary Tree 대신 선택된 이유, 디스크 I/O 최소화를 위한 높은 Fan-out 구조, Clustered Index가 PK 순서로 데이터를 물리적으로 배치하는 원리 |
| "EXPLAIN으로 실행계획을 확인하세요" | Cost-Based Optimizer가 통계 정보(rows, cardinality)를 기반으로 인덱스 선택 비용을 계산하는 방법, type: ALL vs type: ref 차이의 실제 의미 |
| "격리 수준을 REPEATABLE_READ로 설정하세요" | MVCC가 Undo Log로 스냅샷 시점의 이전 버전을 보관하는 방식, trx_id와 Read View로 어느 버전을 읽을지 결정하는 원리 |
| "데드락이 발생하면 재시도하세요" | Next-Key Lock의 범위가 결정되는 조건, InnoDB 데드락 감지 알고리즘(Wait-for Graph), 데드락 로그에서 어느 트랜잭션이 어떤 락을 잡고 있었는지 분석하는 방법 |
| "N+1 문제는 Fetch Join으로 해결합니다" | N+1이 발생할 때 Slow Query Log에 어떻게 기록되는지, DB 관점에서 수십 개의 독립 쿼리가 Connection Pool에 미치는 영향 |
| 이론 나열 | 실행 가능한 EXPLAIN 분석 + Slow Query Log 재현 + 락 확인 쿼리 + Docker Compose 실험 환경 + Spring Data JPA 연결 |
각 챕터의 첫 문서부터 바로 학습을 시작하세요!
💡 각 섹션을 클릭하면 상세 문서 목록이 펼쳐집니다
핵심 질문: MySQL은 데이터를 디스크에 어떻게 저장하고, 메모리에서 어떻게 관리하며, 장애 시 어떻게 복구하는가?
Page·Block 구조부터 WAL(Write-Ahead Logging)까지 (5개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. Page·Block·Extent — InnoDB 물리 저장 구조 | InnoDB가 데이터를 16KB Page 단위로 저장하는 이유, Page → Extent → Segment 계층 구조, 디스크 I/O가 Page 단위로 발생하는 원리와 Random I/O vs Sequential I/O 비용 차이 |
| 02. InnoDB Buffer Pool — 메모리와 디스크 사이 캐시 레이어 | Buffer Pool이 LRU 알고리즘으로 Page를 교체하는 방식, Young/Old 서브리스트 구조, innodb_buffer_pool_size 튜닝 기준, Dirty Page가 디스크에 플러시되는 시점 — JPA 쓰기 지연과의 연결 |
| 03. Row Format — 데이터가 Page 안에 저장되는 구조 | COMPACT / DYNAMIC / COMPRESSED Row Format 비교, NULL 비트맵이 저장 공간을 절약하는 방식, VARCHAR와 TEXT의 오프-페이지 저장(External Storage) 조건, 실제 Row 크기 계산 방법 |
| 04. Tablespace와 파일 구성 | System Tablespace vs File-Per-Table Tablespace 차이, .ibd 파일 내부 구조, Undo Tablespace와 Temporary Tablespace의 역할, innodb_file_per_table 설정 선택 기준 |
| 05. WAL — Write-Ahead Logging과 장애 복구 | WAL이 "디스크에 먼저 쓰지 않아도 되는 이유"를 가능하게 하는 원리, Redo Log로 커밋된 트랜잭션을 재현하는 Crash Recovery 과정, innodb_flush_log_at_trx_commit 설정과 내구성 트레이드오프 |
핵심 질문: 인덱스를 걸었는데 쿼리가 왜 느리고, Optimizer는 언제 인덱스를 타지 않으며, Covering Index는 왜 빠른가?
B-Tree 원리부터 인덱스 설계 전략까지 (7개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. B-Tree 인덱스 — 왜 Binary Tree가 아닌 B-Tree인가 | 디스크 I/O 횟수를 최소화하기 위해 Node 하나에 수백 개의 키를 담는 높은 Fan-out 구조, Binary Tree vs B-Tree 탐색 비용 비교, B+Tree에서 데이터가 Leaf Node에만 존재하는 이유, 범위 스캔이 Leaf 레벨 연결 리스트를 순회하는 방식 |
| 02. Clustered Index vs Non-Clustered Index | Clustered Index가 PK 순서로 데이터를 물리적으로 배치하는 원리, Secondary Index가 PK를 포인터로 저장하는 이유, Secondary Index로 조회 시 발생하는 이중 탐색(Double Lookup), PK 선택이 삽입 성능에 미치는 영향 — JPA @Id 전략과의 연결 |
| 03. Covering Index와 Index-Only Scan | Extra 컬럼에 Using index가 표시되는 조건, Covering Index가 테이블 접근 없이 인덱스만으로 결과를 반환하는 원리, SELECT 컬럼에 따라 Covering 여부가 달라지는 이유, JPA @Query에서 Covering Index를 활용하는 패턴 |
| 04. Composite Index — 컬럼 순서가 왜 중요한가 | 좌측 접두사 규칙(Leftmost Prefix Rule)이 B-Tree 구조에서 도출되는 원리, (a, b, c) 인덱스에서 WHERE b = ?가 인덱스를 타지 않는 이유, 카디널리티 순서 vs 쿼리 패턴 순서 선택 기준, EXPLAIN의 key_len으로 인덱스 사용 범위 확인하는 방법 |
| 05. Index Selectivity와 Cardinality | Cardinality가 낮은 컬럼(성별, 상태)에 인덱스를 걸어도 Full Scan보다 느릴 수 있는 이유, Optimizer가 통계 정보를 기반으로 인덱스 사용 여부를 결정하는 비용 모델, ANALYZE TABLE로 통계를 갱신해야 하는 시점 |
| 06. 인덱스를 타지 않는 쿼리 패턴 | 함수 적용(WHERE YEAR(created_at) = 2024), 형변환(WHERE user_id = '1'), LIKE '%keyword', OR 조건이 인덱스를 무력화하는 내부 원리, JPA에서 자주 발생하는 인덱스 미사용 패턴 |
| 07. 인덱스 설계 전략 — 언제 걸고 언제 걸지 않는가 | 인덱스 추가가 INSERT / UPDATE / DELETE 성능을 저하시키는 이유(B-Tree 재균형), 인덱스 유지 비용 vs 조회 이득의 트레이드오프 계산, 복합 인덱스 vs 단일 인덱스 선택 기준, 실무에서 인덱스를 제거해야 하는 신호 |
핵심 질문: SQL이 실행되는 순간 Parse → Optimize → Execute 각 단계에서 무슨 일이 일어나고, Optimizer는 어떤 기준으로 실행계획을 선택하는가?
쿼리 실행 단계부터 힌트·강제 인덱스까지 (6개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. 쿼리 실행 단계 — Parse → Optimize → Execute | SQL 문자열이 Parse Tree로 변환되는 과정, Logical Plan을 Physical Plan으로 변환하는 Optimization 단계, Handler_read_* 상태 변수로 실제 실행 경로를 확인하는 방법 |
| 02. EXPLAIN과 EXPLAIN ANALYZE — 실행계획 읽는 법 | type, key, rows, Extra 컬럼의 의미를 InnoDB 내부 동작과 연결해서 해석하는 방법, EXPLAIN ANALYZE의 실제 실행 비용과 예측 비용 비교, Using filesort / Using temporary 가 발생하는 조건 |
| 03. Cost-Based Optimizer — 인덱스 선택 원리 | Optimizer가 rows × cost_per_row로 각 실행계획의 비용을 계산하는 방식, mysql.server_cost / mysql.engine_cost 테이블의 비용 상수, 통계 정보(페이지 수, 카디널리티)가 부정확할 때 잘못된 실행계획이 선택되는 시나리오 |
| 04. Join 알고리즘 — Nested Loop, Hash Join, Sort Merge Join | Nested Loop Join이 인덱스 없이 O(n²)이 되는 원리, MySQL 8.0에서 추가된 Hash Join의 동작 방식과 적합한 상황, Join 순서(Driving Table)가 성능에 미치는 영향, JPA Fetch Join이 실제로 생성하는 SQL과 실행계획 |
| 05. Statistics — Optimizer의 판단 근거 | information_schema.INNODB_TABLE_STATS의 n_rows, clustered_index_size 등이 어떻게 수집되는지, 통계 자동 갱신 조건과 innodb_stats_auto_recalc 설정, 대량 데이터 변경 후 실행계획이 달라지는 원인 분석 |
| 06. 힌트와 강제 인덱스 사용 | FORCE INDEX / USE INDEX / IGNORE INDEX 문법과 Optimizer가 무시하는 경우, MySQL 8.0의 Optimizer Hint(/*+ INDEX(...) */) 방식, JPA에서 힌트를 적용하는 @QueryHints 패턴, 힌트가 필요 없도록 통계를 정비하는 근본적 해결책 |
핵심 질문:
REPEATABLE_READ와READ_COMMITTED가 다르게 동작하는 이유는 무엇이고, MVCC는 락 없이 어떻게 일관된 읽기를 보장하는가?
ACID 구현 원리부터 Redo Log 내구성까지 (7개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. ACID — 교과서 정의와 실제 구현 사이 | Atomicity를 Undo Log로, Durability를 Redo Log로 구현하는 방식, Consistency가 DB 엔진이 아닌 애플리케이션 책임인 이유, Isolation이 격리 수준 선택 문제로 환원되는 구조 — Spring @Transactional이 이 중 어느 부분을 담당하는가 |
| 02. MVCC — 락 없이 읽기 일관성을 보장하는 원리 | 각 Row에 DB_TRX_ID / DB_ROLL_PTR 숨겨진 컬럼이 저장되는 방식, Read View가 생성 시점의 활성 트랜잭션 목록을 기록해 어느 버전을 읽을지 결정하는 원리, 읽기가 쓰기를 차단하지 않는 이유 |
| 03. Undo Log — 이전 버전을 보관하는 방법 | Undo Log가 DB_ROLL_PTR로 연결된 버전 체인(Version Chain)을 형성하는 구조, 롤백 시 Undo Log를 역방향으로 적용하는 과정, 오래된 Read View가 남아있을 때 Undo Log가 삭제되지 않아 Tablespace가 팽창하는 문제 |
| 04. 트랜잭션 격리 수준 4가지 — 구현 원리까지 | READ_UNCOMMITTED / READ_COMMITTED / REPEATABLE_READ / SERIALIZABLE 각각이 Read View를 언제 생성하는지의 차이, READ_COMMITTED에서 Non-Repeatable Read가 발생하는 구체적 원리, JPA @Transactional(isolation = ...) 설정과 실제 DB 격리 수준의 관계 |
| 05. Phantom Read — 발생 조건과 방지 방법 | Phantom Read가 같은 범위 쿼리에서 다른 행이 나타나는 이유, REPEATABLE_READ에서 Consistent Read는 막지만 Current Read(잠금 읽기)에서 발생하는 Phantom Read, Gap Lock이 삽입을 차단해 Phantom Read를 방지하는 메커니즘 |
| 06. Consistent Read vs Current Read | SELECT(스냅샷 읽기)와 SELECT ... FOR UPDATE / SELECT ... FOR SHARE(잠금 읽기)가 내부적으로 다른 경로를 타는 이유, Consistent Read는 Undo Log 버전을 읽지만 Current Read는 최신 버전을 읽고 락을 거는 차이 — JPA @Lock 어노테이션과의 연결 |
| 07. Redo Log와 트랜잭션 내구성 | COMMIT 시 Redo Log가 먼저 디스크에 flush되는 이유(WAL), Redo Log Buffer → Redo Log File 플러시 시점, innodb_flush_log_at_trx_commit 0/1/2 설정의 내구성 vs 성능 트레이드오프, Group Commit으로 IOPS를 줄이는 원리 |
핵심 질문: InnoDB는 언제 Row Lock을, 언제 Gap Lock을 걸며, 데드락은 어떤 순서로 발생하고 InnoDB는 어떻게 감지하는가?
락의 종류부터 Optimistic vs Pessimistic 선택 기준까지 (6개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. 락의 종류 — Shared, Exclusive, Intention Lock | S Lock(공유 잠금)과 X Lock(배타 잠금)의 호환 행렬, IS / IX Intention Lock이 테이블-레벨에서 Row Lock 의도를 표시하는 이유, performance_schema.data_locks로 현재 락 상태를 확인하는 방법 |
| 02. Row Lock vs Table Lock — InnoDB가 락을 거는 단위 | InnoDB가 인덱스 레코드에 락을 거는 원리(인덱스가 없으면 Full Table Lock), LOCK TABLES vs InnoDB Row Lock 비교, information_schema.INNODB_TRX로 트랜잭션별 락 보유 현황 조회 방법 |
| 03. Gap Lock과 Next-Key Lock — Phantom Read 방지 메커니즘 | Gap Lock이 인덱스 레코드 사이의 "간격"을 잠가 새로운 삽입을 차단하는 방식, Next-Key Lock = Record Lock + Gap Lock의 범위 계산 방법, READ_COMMITTED에서 Gap Lock이 비활성화되는 이유와 그 결과 |
| 04. 데드락 발생 원리와 감지 알고리즘 | 두 트랜잭션이 서로의 락을 기다리는 순환 의존(Circular Wait) 형성 과정, InnoDB의 Wait-for Graph 기반 데드락 감지 알고리즘, 롤백될 트랜잭션을 선택하는 innodb_deadlock_detect 비용 계산 |
| 05. 데드락 로그 분석과 해결 전략 | SHOW ENGINE INNODB STATUS의 LATEST DETECTED DEADLOCK 섹션을 단계별로 해석하는 방법, 어느 트랜잭션이 어느 인덱스 레코드에 어떤 락을 보유·대기 중인지 읽는 방법, 데드락 재현 → 로그 확인 → 락 순서 통일로 해결하는 실전 패턴 |
| 06. Optimistic Lock vs Pessimistic Lock — 선택 기준 | Pessimistic Lock(SELECT ... FOR UPDATE)이 충돌을 사전 차단하는 방식과 처리량 저하 대가, Optimistic Lock(버전 컬럼 CAS)이 충돌을 사후 감지하는 방식과 재시도 비용, JPA @Version이 UPDATE WHERE version = ? 로 변환되는 원리, 쓰기 충돌 빈도에 따른 선택 기준 |
핵심 질문: 느린 쿼리를 어떻게 찾고, N+1은 DB 관점에서 왜 문제이며, OFFSET 기반 페이징은 왜 데이터가 많을수록 느려지는가?
Slow Query 분석부터 HikariCP 튜닝까지 (5개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. Slow Query 분석 방법론 | slow_query_log 설정과 long_query_time 임계값 선택 기준, mysqldumpslow로 패턴별 슬로우 쿼리를 집계하는 방법, performance_schema의 events_statements_summary_by_digest로 상위 쿼리를 찾는 방법 |
| 02. N+1 문제 — DB 관점에서의 근본 원인 | N+1이 발생할 때 Slow Query Log에 동일 쿼리가 N번 반복 기록되는 패턴, 각 쿼리가 독립 Connection을 사용할 때 Connection Pool 소진과의 관계, p6spy / datasource-proxy로 JPA가 날리는 쿼리 수를 계측하는 방법, Fetch Join과 @EntityGraph가 생성하는 실행계획 비교 |
| 03. 페이징 최적화 — OFFSET의 함정과 커서 기반 페이징 | LIMIT 10 OFFSET 100000이 실제로 100010행을 읽고 100000행을 버리는 원리, No-Offset(커서 기반) 페이징이 WHERE id > last_id로 B-Tree 탐색을 재시작하는 방식, JPA에서 커서 기반 페이징을 구현하는 패턴 |
| 04. 대용량 테이블 최적화 — 파티셔닝과 샤딩 | Range / List / Hash Partition이 파티션 프루닝(Partition Pruning)으로 스캔 범위를 줄이는 원리, 파티션 키 선택이 잘못됐을 때 전체 파티션을 스캔하는 문제, 수직 샤딩(컬럼 분리)과 수평 샤딩(행 분리)의 트레이드오프 |
| 05. Connection Pool 튜닝 — HikariCP와 DB 연결 관리 | HikariCP가 Connection을 미리 생성해두는 방식, maximumPoolSize 계산 공식(CPU 코어 수 × 2 + 유효 디스크 수), Connection 대기로 인한 HikariPool-1 - Connection is not available 에러 분석, Spring Boot에서 HikariCP 설정과 모니터링 방법 |
핵심 질문: Primary에 쓰인 데이터가 Replica에 어떻게 전파되고, Read Replica 사용 시 정합성은 어떻게 보장되는가?
Binary Log 복제 원리부터 Spring Read/Write 분리까지 (4개 문서)
| 문서 | 다루는 내용 |
|---|---|
| 01. MySQL Replication 원리 — Binary Log 기반 복제 | Primary가 Binary Log에 변경 이벤트를 기록하고, Replica의 IO Thread가 Relay Log로 수신하며, SQL Thread가 적용하는 3단계 구조, Statement-Based vs Row-Based vs Mixed 바이너리 로그 포맷 비교 |
| 02. Read Replica — 읽기 분산과 정합성 트레이드오프 | Replication Lag이 발생하는 이유와 측정 방법(Seconds_Behind_Source), 방금 쓴 데이터를 Read Replica에서 읽었을 때 보이지 않는 Stale Read 문제, 쓰기 직후 읽기는 Primary로, 이후 읽기는 Replica로 라우팅하는 전략 |
| 03. GTID 기반 복제와 자동 페일오버 | GTID(Global Transaction ID)가 server_uuid:transaction_id 형태로 트랜잭션을 전역 고유 식별하는 방식, Replica가 어디까지 적용했는지 추적하는 gtid_executed, MHA / Orchestrator로 자동 페일오버 시 GTID가 중요한 이유 |
| 04. Spring에서 Read/Write 분리 구현 | AbstractRoutingDataSource로 트랜잭션 속성(readOnly = true)에 따라 DataSource를 동적으로 선택하는 패턴, @Transactional(readOnly = true)가 MySQL에 SET TRANSACTION READ ONLY를 전송해 MVCC 최적화를 활성화하는 원리, LazyConnectionDataSourceProxy와의 조합 |
이 레포의 모든 챕터는 아래 흐름을 완전히 이해하는 것을 목표로 합니다.
애플리케이션 (JPA / JDBC)
│ SQL 문자열 전송
▼
┌─────────────────────────────────────────┐
│ MySQL Server Layer │
│ ① Parser SQL → Parse Tree │
│ ② Optimizer 실행계획 수립 (Ch3) │
│ ├── 통계 정보 조회 (rows, cardinality) │
│ ├── 인덱스 선택 비용 계산 │
│ └── Join 순서 결정 │
│ ③ Executor 실행계획 실행 │
└──────────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ InnoDB Storage Engine │
│ │
│ Buffer Pool (Ch1) │
│ ├── Page Cache (16KB 단위) │
│ └── Dirty Page → Disk (비동기) │
│ │
│ B-Tree Index 탐색 (Ch2) │
│ ├── Clustered Index (PK → Row) │
│ └── Secondary Index → PK → Row │
│ │
│ MVCC (Ch4) │
│ ├── Read View 생성 (격리 수준별) │
│ ├── Undo Log 버전 체인 탐색 │
│ └── Consistent Read / Current Read │
│ │
│ Lock Manager (Ch5) │
│ ├── Record Lock / Gap Lock 획득 │
│ └── Wait-for Graph 데드락 감지 │
│ │
│ Disk I/O │
│ ├── Redo Log flush (COMMIT 시) │
│ └── Page 읽기/쓰기 (Page 단위) │
└─────────────────────────────────────────┘
트랜잭션 생명주기:
BEGIN
→ Read View 생성 (REPEATABLE_READ 기준)
→ 쿼리 실행: B-Tree 탐색 → Lock 획득
→ 변경: Undo Log 기록 → Buffer Pool 갱신
COMMIT
→ Redo Log flush → Lock 해제
→ Buffer Pool → Disk (비동기, Checkpoint)
장애 시나리오:
① 인덱스 없는 쿼리 → Full Table Scan → Slow Query Log 기록
② 격리 수준별 이상현상 → Dirty / Phantom Read 재현 → MVCC로 분석
③ 데드락 발생 → Wait-for Graph 감지 → ROLLBACK → 로그 분석
④ N+1 발생 → Slow Query Log N번 반복 → Fetch Join으로 해결
모든 챕터의 실험은 아래 환경에서 재현 가능합니다.
# docker-compose.yml
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: deep_dive
volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql # 실험용 테스트 데이터
ports:
- "3306:3306"
command: >
--slow_query_log=ON
--slow_query_log_file=/var/log/mysql/slow.log
--long_query_time=0.1
--innodb_monitor_enable=all
--general_log=ON
--general_log_file=/var/log/mysql/general.log-- init.sql: 공통 실험 데이터
-- 인덱스 실험: 100만 건 주문 데이터
-- 트랜잭션 실험: 재고 감소 동시성 시나리오
-- 데드락 실험: 두 트랜잭션의 교차 업데이트 시나리오
-- 페이징 실험: OFFSET vs 커서 기반 성능 비교각 문서에서 해당 시나리오에 필요한 쿼리와 실험 순서를 안내합니다.
모든 문서는 동일한 구조로 작성됩니다.
| 섹션 | 설명 |
|---|---|
| 🎯 핵심 질문 | 이 문서를 읽고 나면 답할 수 있는 질문 |
| 🔍 왜 이 개념이 중요한가 | 실무에서 마주치는 문제 상황과 이 개념의 연결 |
| 😱 잘못된 이해 | Before — DB를 블랙박스로 두는 접근과 그 결과 |
| ✨ 올바른 이해 | After — 원리를 알고 난 후의 접근과 해석 |
| 🔬 내부 동작 원리 | MySQL InnoDB 소스 레벨 분석 + ASCII 구조도 |
| 💻 실전 실험 | EXPLAIN 분석, 슬로우 쿼리 재현, 락 확인 쿼리 |
| 📊 성능 비교 | 인덱스 유무, 격리 수준별, 락 전략별 수치 비교 |
| ⚖️ 트레이드오프 | 이 설계의 장단점, 언제 다른 접근을 택할 것인가 |
| 📌 핵심 정리 | 한 화면 요약 |
| 🤔 생각해볼 문제 | 개념을 더 깊이 이해하기 위한 질문 + 해설 |
🟢 "느린 쿼리를 당장 분석해야 한다" — 실전 긴급 투입 (3일)
Day 1 Ch3-02 EXPLAIN 읽는 법 → 실행계획 해석
Day 2 Ch2-01 B-Tree 인덱스 원리 → 인덱스 선택 이유 이해
Ch2-06 인덱스를 타지 않는 패턴 → 현재 쿼리 점검
Day 3 Ch6-01 Slow Query 분석 방법론 → 실제 서비스에 적용
🟡 "트랜잭션과 락을 제대로 이해하고 싶다" — 핵심 집중 (1주)
Day 1 Ch4-01 ACID 실제 구현 방법
Day 2 Ch4-02 MVCC 동작 원리 → Read View 이해
Ch4-03 Undo Log 버전 체인
Day 3 Ch4-04 격리 수준 4가지 — 구현 원리까지
Day 4 Ch5-01 락의 종류 (S/X/IS/IX)
Ch5-03 Gap Lock과 Next-Key Lock
Day 5 Ch5-04 데드락 발생 원리
Ch5-05 데드락 로그 분석 실습
Day 6 Ch6-02 N+1 문제 — DB 관점 분석
Day 7 Ch7-04 Spring Read/Write 분리 구현
🔴 "MySQL InnoDB 소스코드를 직접 읽고 내부를 완전히 이해하고 싶다" — 전체 정복 (7주)
1주차 Chapter 1 전체 — InnoDB 물리 저장 구조
→ Buffer Pool의 LRU 교체 정책을 performance_schema로 실시간 관찰
2주차 Chapter 2 전체 — Index 완전 분해
→ EXPLAIN으로 각 인덱스 전략별 type, key_len 변화 확인
3주차 Chapter 3 전체 — Query Execution & Optimizer
→ EXPLAIN ANALYZE로 예측 비용 vs 실제 비용 오차 분석
4주차 Chapter 4 전체 — Transaction & MVCC
→ 격리 수준별 Phantom Read 재현 실험 → Undo Log 버전 체인 추적
5주차 Chapter 5 전체 — Lock & Concurrency
→ 데드락 의도적으로 발생 → SHOW ENGINE INNODB STATUS로 로그 분석
6주차 Chapter 6 전체 — Performance Tuning
→ N+1을 Slow Query Log로 포착 → Fetch Join으로 해결 → 비교
7주차 Chapter 7 전체 — Replication & High Availability
→ Docker Compose로 Primary/Replica 구성 → Replication Lag 측정
| 레포 | 주요 내용 | 연관 챕터 |
|---|---|---|
| spring-data-transaction-deep-dive | JPA 영속성 컨텍스트, @Transactional 전파, JPQL |
Ch4(MVCC와 JPA 격리 수준 매핑), Ch5(Optimistic Lock @Version), Ch6(N+1 해결) |
| spring-core-deep-dive | AOP Proxy, Bean 생명주기 | Ch5(@Transactional이 Proxy로 트랜잭션 경계를 만드는 원리) |
| spring-boot-internals | Auto-configuration, Actuator | Ch6(HikariCP Auto-configuration, spring.datasource 설정) |
💡 이 레포는 MySQL InnoDB 내부 동작에 집중합니다. JPA/Spring을 모르더라도 순수 DB 관점으로 학습 가능합니다. 단, Chapter 6의 N+1 분석과 Chapter 7의 Read/Write 분리는 Spring Data JPA 선행 지식이 있을 때 더 깊이 연결됩니다.
- MySQL 8.0 Reference Manual — InnoDB Storage Engine
- MySQL 8.0 Reference Manual — EXPLAIN Output Format
- InnoDB Source Code (GitHub)
- High Performance MySQL, 4th Edition
- Use The Index, Luke — SQL Indexing and Tuning
- MySQL InnoDB Internals (Percona Blog)
- Jeremy Cole — InnoDB Ruby (Page 구조 분석 도구)