# 발표자 노트 — SQL 한 문장이 InnoDB 페이지를 만날 때까지

예상 발표 시간: 32–38분

## 1. SQL 한 문장이 InnoDB 페이지를 만날 때까지

- 목적/청중: MySQL을 사용해 봤지만 내부 계층을 한 번에 연결해 본 적 없는 개발자에게 오늘의 약속을 제시합니다.
- 발표 멘트: 오늘은 구성요소 이름을 외우는 시간이 아닙니다. `SELECT` 한 문장이 들어와 Connection을 만들고 SQL 계층을 거쳐 InnoDB 페이지를 읽은 뒤 결과로 돌아오는 길을 따라가겠습니다. 이 경로를 잡으면 메모리, 실행 계획, 인덱스를 따로 공부할 때도 각 개념의 위치를 놓치지 않습니다.
- 강조: 우측의 네 노드를 위에서 아래로 짚습니다.
- 전환: 먼저 전체 경로를 네 구간으로 나눠 보겠습니다.
- 출처/주의: Real MySQL 8.0의 요청 범위를 MySQL 8.0 공식 문서로 교차 검증했습니다.

## 2. 오늘 따라갈 네 개의 경계

- 목적/청중: 발표의 전체 구조를 미리 보여줘 세부 내용의 위치를 잡게 합니다.
- 발표 멘트: 경계는 연결, SQL 처리, 엔진 인터페이스, 저장 구조 네 곳입니다. 앞쪽 두 구간은 MySQL 서버의 공통 책임이고, 뒤쪽은 InnoDB가 실제 데이터를 다루는 영역입니다. 마지막에 이 네 구간을 한 번 더 연결해 보겠습니다.
- 강조: 2번 SQL 처리와 3번 엔진 경계 사이를 잠시 멈춰 짚습니다.
- 전환: 이 경계를 이해하려면 MySQL 서버와 InnoDB를 같은 것으로 보지 않아야 합니다.
- 출처/주의: 네 구간은 교육용 묶음이며 실제 내부 함수 경계와 일대일 대응하지 않습니다.

## 3. MySQL 서버와 InnoDB의 역할 경계

- 목적/청중: 서버 공통 계층과 저장 엔진의 책임을 구분합니다.
- 발표 멘트: MySQL 서버는 연결, 권한, SQL 해석, 최적화, 실행 흐름을 제공합니다. InnoDB는 그 아래에서 테이블·인덱스·레코드·페이지를 다룹니다. 옵티마이저가 계획을 만든다고 해서 직접 `.ibd` 파일을 읽는 것은 아닙니다. 실행기는 Handler API로 엔진에 작업을 요청합니다.
- 강조: 파란 MySQL Server 층과 Handler API 층의 경계입니다.
- 전환: 이 인터페이스 덕분에 MySQL은 엔진을 바꿔 끼울 수 있습니다.
- 출처/주의: InnoDB가 기본 엔진이어도 MySQL 서버 전체와 동의어는 아닙니다.

## 4. Plugin Storage Engine 구조

- 목적/청중: 플러그형 스토리지 엔진 구조를 공식 그림으로 확인합니다.
- 발표 멘트: 위쪽 Connector와 SQL 계층은 공통이고, 아래쪽 엔진은 테이블별 요구에 맞춰 선택할 수 있습니다. 다만 공통 인터페이스가 기능 차이까지 없애 주지는 않습니다. InnoDB는 트랜잭션·행 잠금·외래 키를 제공하고, MyISAM은 비트랜잭션·테이블 잠금, MEMORY는 메모리 기반 저장에 초점을 둡니다. `SHOW ENGINES`로 실제 서버의 지원 상태를 확인해야 합니다.
- 강조: 그림 중앙의 Pluggable Storage Engines 층입니다.
- 전환: 엔진을 만나기 전에 쿼리는 먼저 서버 연결 안으로 들어옵니다.
- 출처/주의: MySQL 8.0 Reference Manual Figure 18.3, © Oracle. 공개·상업적 재배포에는 별도 권리 검토가 필요합니다.

## 5. Client → Connection → Session → Thread

- 목적/청중: 자주 섞어 쓰는 네 용어의 수명과 역할을 구분합니다.
- 발표 멘트: Client는 외부 프로그램, Connection은 통신 채널, Session은 변수와 트랜잭션 상태를 가진 논리 문맥입니다. Thread는 요청을 실제로 수행하는 실행 단위입니다. 기본 모델에서는 연결마다 처리 스레드가 붙지만 Thread Pool을 쓰면 세션과 OS 스레드가 평생 일대일로 고정되지 않습니다.
- 강조: Connection과 Session을 별도 노드로 둔 이유를 설명합니다.
- 전환: 실행 단위가 생기면 다음 질문은 메모리를 무엇과 공유하는가입니다.
- 출처/주의: connection pool을 쓰는 애플리케이션의 논리 요청 수명과 서버 연결 수명도 다를 수 있습니다.

## 6. Global Memory vs Session Memory

- 목적/청중: 메모리 사용량을 공유 영역과 작업별 영역으로 나눠 보게 합니다.
- 발표 멘트: Buffer Pool은 서버 전체가 공유하지만 sort, join 같은 버퍼는 필요한 연산이 생길 때 세션이나 문장 단위로 사용됩니다. 그래서 모든 최대 버퍼가 모든 연결에 항상 선할당된다고 계산하면 실제 사용량과 어긋납니다. 반대로 한 문장이 여러 join buffer를 쓸 수 있다는 점도 놓치면 안 됩니다.
- 강조: 우측의 Session baseline과 Statement/operation을 구분합니다.
- 전환: 이제 연결과 메모리 위에서 SQL이 실제로 처리되는 순서를 보겠습니다.
- 출처/주의: `sort_buffer_size`는 MySQL 8.0.12부터 필요한 만큼 증가시키는 방식입니다.

## 7. SQL Query 처리 흐름

- 목적/청중: 사용자 요청의 Parser, Preprocessor, Optimizer, Executor를 공식 용어와 연결합니다.
- 발표 멘트: 교육용으로는 Parser, Preprocessor, Optimizer, Executor 순서가 익숙합니다. 공식 구현에서는 Preprocessor라는 하나의 고정 컴포넌트보다 Resolver와 Preparation, 권한·의미 검사를 묶어 설명하는 편이 정확합니다. 그 뒤 옵티마이저가 계획을 고르고 실행기가 Handler API를 호출합니다.
- 강조: Resolver/Preparation 상자의 보조 설명입니다.
- 전환: Parser와 Resolver가 무엇을 다르게 실패시키는지 보겠습니다.
- 출처/주의: MySQL 8.0.46 Source Code Documentation의 DML 처리 단계와 교차 검증했습니다.

## 8. Parser와 Resolver/Preparation

- 목적/청중: 문법 오류와 의미 해석 오류를 구분합니다.
- 발표 멘트: `FORM`처럼 문법 자체가 틀리면 Parser에서 멈춥니다. 문법은 맞지만 존재하지 않는 열을 참조하면 Resolver/Preparation에서 실패합니다. 이 단계는 테이블, 열, 별칭, 뷰를 해석하고 권한도 확인합니다. 책에서 말하는 Preprocessor를 이 범주로 이해하면 됩니다.
- 강조: 두 코드 예시의 오류 지점을 비교합니다.
- 전환: 구조와 의미가 확정되면 가능한 실행 방법 중 하나를 골라야 합니다.
- 출처/주의: 내부 구현 명칭과 함수 경계는 마이너 버전에서 달라질 수 있습니다.

## 9. Optimizer가 고르는 실행 계획

- 목적/청중: 옵티마이저가 인덱스 하나만 고르는 구성요소가 아님을 설명합니다.
- 발표 멘트: 옵티마이저는 테이블 접근 방식, 인덱스, 조인 순서, 서브쿼리 변환과 materialization 같은 후보를 비용으로 비교합니다. 그림에서는 index와 join을 함께 쓴 계획이 선택됐지만 실제 선택은 통계와 조건에 따라 바뀝니다. `EXPLAIN`은 예상 계획이고 `EXPLAIN ANALYZE`는 문장을 실제로 실행합니다.
- 강조: CHOSEN 표시와 cost는 개념 예시이며 실측값이 아님을 밝힙니다.
- 전환: 계획을 골랐다면 누군가 그 iterator를 실제로 돌려야 합니다.
- 출처/주의: MySQL 8.0에는 hash join도 있으므로 모든 조인을 nested loop라고 단정하지 않습니다.

## 10. Executor와 Storage Engine의 핸드셰이크

- 목적/청중: 실행 제어와 데이터 접근 책임을 분리합니다.
- 발표 멘트: Executor는 계획의 각 연산을 순서대로 호출하고 조건, 조인, 정렬, 집계 흐름을 제어합니다. 행이나 인덱스 레코드가 필요하면 Handler API로 InnoDB에 요청합니다. InnoDB는 적절한 인덱스와 페이지를 찾아 결과를 다시 올려보냅니다.
- 강조: 가운데 Handler API 원을 경계선처럼 짚습니다.
- 전환: 이제 그 경계 아래 InnoDB 내부로 내려갑니다.
- 출처/주의: 실제 executor는 iterator 기반이며 연산별 세부 호출은 계획에 따라 달라집니다.

## 11. InnoDB Architecture

- 목적/청중: InnoDB의 메모리·디스크 구조를 한 장에서 조망합니다.
- 발표 멘트: InnoDB는 파일 포맷 하나가 아닙니다. 메모리에는 Buffer Pool, Change Buffer, Adaptive Hash Index, Log Buffer가 있고 디스크에는 Tablespace, Redo Log, Doublewrite 구조가 있습니다. Adaptive Hash Index는 반복되는 B-tree 접근을 관찰해 hash lookup을 보조하지만 효과는 워크로드에 따라 다릅니다. 이번 발표에서는 쿼리 읽기 경로와 직접 연결되는 Buffer Pool, Tablespace, Page로 좁혀 갑니다.
- 강조: 공식 그림의 In-Memory와 On-Disk 구분선입니다.
- 전환: 가장 먼저 요청이 만나는 페이지 캐시부터 보겠습니다.
- 출처/주의: MySQL 8.0 Reference Manual Figure 17.1, © Oracle.

## 12. Buffer Pool에서 읽고 쓰기

- 목적/청중: Buffer Pool을 결과 캐시가 아닌 페이지 캐시로 이해시킵니다.
- 발표 멘트: 필요한 페이지가 메모리에 있으면 바로 사용하고, 없으면 Tablespace에서 읽습니다. 수정된 페이지는 dirty 상태로 메모리에 남았다가 flush됩니다. LRU 변형과 midpoint insertion은 큰 스캔이 자주 쓰는 페이지를 한꺼번에 밀어내는 일을 줄입니다.
- 강조: 공식 그림의 young/old sublist와 midpoint 위치입니다.
- 전환: Cache miss 때 읽어 오는 페이지는 어떤 저장 공간에 속할까요?
- 출처/주의: Buffer Pool은 query result cache가 아니며 테이블 전체를 무조건 올리지 않습니다.

## 13. Tablespace 지형도

- 목적/청중: Tablespace와 파일을 단순히 동일시하지 않게 합니다.
- 발표 멘트: Tablespace는 페이지를 담는 논리 저장 공간입니다. file-per-table은 보통 한 테이블의 데이터와 인덱스를 하나의 `.ibd` 파일에 둡니다. 반면 System과 General Tablespace는 여러 테이블이나 내부 구조를 공유할 수 있습니다. Undo와 Temporary도 목적이 다릅니다.
- 강조: file-per-table과 general/system의 공유 범위를 비교합니다.
- 전환: Tablespace 안을 더 잘게 나누면 Page, Extent, Segment가 나옵니다.
- 출처/주의: MySQL 8.0의 통합 Data Dictionary를 구버전 System Tablespace 설명과 섞지 않습니다.

## 14. Page → Extent → Segment

- 목적/청중: InnoDB가 공간과 I/O를 관리하는 기본 단위를 설명합니다.
- 발표 멘트: Page가 Buffer Pool과 디스크 I/O의 공통 단위입니다. 기본 16KB 설정에서는 64개 페이지가 1MB Extent를 이룹니다. Segment는 B-tree 같은 파일 구조가 여러 Extent를 묶어 소유하는 논리 단위입니다. 16KB는 기본값이지 절대값이 아닙니다.
- 강조: 16KB × 64 = 1MB 관계입니다.
- 전환: 페이지 안의 행은 어떤 인덱스 구조로 배치되는지 보겠습니다.
- 출처/주의: `innodb_page_size`는 인스턴스 초기화 시 정하며 운영 중 동적으로 바꾸지 않습니다.

## 15. Primary Key Clustering · Foreign Key

- 목적/청중: Primary Key의 물리 저장 규칙과 InnoDB 외래 키 검사를 한 흐름에서 보여줍니다.
- 발표 멘트: InnoDB의 clustered index 리프 레코드는 행 데이터를 함께 담습니다. 명시한 Primary Key가 있으면 이를 사용하고, 없으면 첫 UNIQUE NOT NULL 인덱스, 그것도 없으면 6-byte 숨은 row ID 기반 인덱스를 만듭니다. InnoDB는 외래 키의 부모·자식 참조 무결성과 cascade 동작도 검사합니다. `foreign_key_checks=0`을 다시 켠다고 기존 행을 소급 검증하지는 않습니다.
- 강조: leaf node의 full row data입니다.
- 전환: 보조 인덱스가 전체 행을 찾을 때 이 PK를 다시 사용합니다.
- 출처/주의: Clustered layout은 SELECT 결과 순서를 보장하지 않습니다.

## 16. Secondary Index의 두 번째 탐색

- 목적/청중: 보조 인덱스 레코드와 clustered index 재탐색을 설명합니다.
- 발표 멘트: InnoDB의 보조 인덱스는 물리 주소 대신 Primary Key 열을 저장합니다. 보조 키로 후보를 찾은 뒤 전체 행이 필요하면 PK로 clustered index를 다시 찾습니다. 그래서 PK가 길면 모든 보조 인덱스가 커집니다. 필요한 열이 보조 인덱스 안에 모두 있으면 커버링 인덱스로 두 번째 탐색을 피할 수 있습니다.
- 강조: 두 B-tree 사이의 화살표입니다.
- 전환: 저장 구조를 마쳤으니 서버 안에 저장되는 SQL 프로그램 객체를 구분하겠습니다.
- 출처/주의: 실제 접근은 옵티마이저 계획과 커버링 여부에 따라 달라집니다.

## 17. Stored Object 용어 지도

- 목적/청중: Procedure, Function, Trigger, Event를 공식 상위 개념 안에 배치합니다.
- 발표 멘트: Stored Procedure와 Stored Function을 합쳐 Stored Routine이라고 합니다. Routine에 Trigger와 Event를 더한 범주가 Stored Program입니다. Stored Object는 여기에 View까지 포함합니다. 비슷한 용어를 호출 방식과 포함 관계로 구분해 두면 권한과 제약 문서를 읽기 쉬워집니다.
- 강조: Routine이 Program 안에 포함된다는 관계입니다.
- 전환: 네 객체의 차이는 이름보다 실행 시점에서 더 선명합니다.
- 출처/주의: View는 사용자 요청의 상세 범위가 아니어서 개념만 표시했습니다.

## 18. Procedure · Function · Trigger · Event

- 목적/청중: 네 객체의 호출 조건, 반환 방식, 운영 위험을 비교합니다.
- 발표 멘트: Procedure는 `CALL`, Function은 식 평가로 실행됩니다. Trigger는 특정 테이블의 DML에 각 행마다 반응하고, Event는 시간이 조건이며 scheduler가 켜져 있어야 합니다. 무엇을 반환하는가보다 누가 언제 실행시키는가를 먼저 보면 선택이 쉬워집니다.
- 강조: Trigger의 FOR EACH ROW와 Event의 `event_scheduler=ON`입니다.
- 전환: 기능 비교를 실제 선택 질문으로 바꿔 보겠습니다.
- 출처/주의: Procedure는 함수식 반환값은 없지만 OUT/INOUT과 result set을 낼 수 있습니다.

## 19. Stored Program 선택 체크리스트

- 목적/청중: DB 내부 로직을 선택할 때 운영 관점의 질문을 제시합니다.
- 발표 멘트: Trigger는 편하지만 호출이 SQL 표면에 보이지 않습니다. Function은 식 안에서 편리하지만 부작용과 binary logging 제약을 확인해야 합니다. Event는 외부 scheduler 없이 동작하지만 scheduler 상태, 권한, 중복 실행을 관찰해야 합니다. 애플리케이션 코드보다 DB 안에 둘 이유가 분명한지 먼저 묻습니다.
- 강조: 호출 가시성과 실행 조건 두 축입니다.
- 전환: 이제 처음의 SELECT를 끝까지 한 번에 연결해 보겠습니다.
- 출처/주의: 저장 프로그램 제약은 복제 방식과 권한 환경에 따라 추가 확인이 필요합니다.

## 20. SELECT 한 문장의 End-to-End 경로

- 목적/청중: 발표의 모든 개념을 하나의 요청 흐름으로 회수합니다.
- 발표 멘트: Client 요청이 Connection과 Session을 통해 들어옵니다. SQL Layer가 parse, resolve, optimize, execute를 수행하고 Handler API가 InnoDB 경계를 엽니다. InnoDB는 Buffer Pool에서 페이지를 찾고 없으면 Tablespace에서 읽습니다. Clustered Index가 행을 찾으면 결과가 같은 경로를 거슬러 Client로 돌아갑니다.
- 강조: 다이어그램의 왼쪽에서 오른쪽으로 한 번에 따라갑니다.
- 전환: 마지막으로 이 흐름을 진단에 쓰는 세 가지 렌즈를 남기겠습니다.
- 출처/주의: 다이어그램은 공식 문서를 근거로 Archify에서 제작했으며 showcase 자동 검증 9/9를 통과했습니다.

## 21. 세 가지 렌즈로 MySQL을 읽는다

- 목적/청중: 구조 이해를 실제 진단 습관으로 바꿉니다.
- 발표 멘트: 첫째, 문제가 서버 공통 계층인지 InnoDB 계층인지 경계를 나눕니다. 둘째, EXPLAIN으로 옵티마이저가 고른 계획을 봅니다. 셋째, 그 계획이 어떤 페이지를 Buffer Pool과 Tablespace에서 읽는지 추적합니다. 이 세 질문만 유지해도 연결 수, 메모리, 인덱스 문제를 한 덩어리로 보지 않게 됩니다.
- 강조: Boundary, Plan, Page 세 제목을 차례로 짚습니다.
- 전환: Q&A로 마무리합니다.
- 출처/주의: 운영 판단은 실제 버전, 에디션, 설정, 워크로드의 측정치와 함께 내려야 합니다.
