InnoDB 기준 → MySQL 거의 표준
스토리지 엔진에서 수행하는 작업 흐름은 다음과 같다.
페이지 찾기 → 수정 → 디스크 쓰기
이 과정에서 Disk I/O를 어떻게 최소화할 것인가가 관건이다.

AHI는 index 고유 Id와 Index Key값을 Hash값으로 만들어 Key로 가진다.
Value값으로는 해당 Index Key값이 존재하는 Leaf Node 주소를 가진다.
AHI를 사용하여 인덱스 B+Tree를 Root부터 탐색하는 비용을 절약한다.
즉, 여기서 Hit가 발생한다면 디스크 I/O 없이 페이지를 바로 읽을 수 있으므로 가장 효율적이다.
메모리 비용이 존재하고, 디스크 I/O가 발생하는 순간 효과가 없으므로 늘 좋진 않다.

AHI에서 Miss가 발생하면 먼저 테이블 딕셔너리를 통해 루트 인덱스 페이지를 찾는다.
B+Tree는 간단하게 인덱스 값으로 정렬된 트리 구조이다.
여기서 InnoDB가 가지는 특징 중 하나는 PK 인덱스 트리의 리프 노드가 실제로 데이터가 존재하는 페이지라는 점이다.
이걸 클러스터드 인덱스라고 부른다. 클러스터드 인덱스가 아닌 경우 리프 노드에는 해당 데이터가 있는 페이지 주소가 있다.
B+Tree를 탐색하여 접근하려는 인덱스에 해당하는 페이지를 찾고 로드한다.

MySQL InnoDB는 두 개의 로그를 유지한다.
Undo log는 MVCC, 롤백을 위해 존재한다.
Redo log는 쿼리 실행 도중에 장애가 발생했을 때, 실행중이던 쿼리의 완료를 보장하기 위해 존재한다.
이 효과를 이해하기 위해서는 InnoDB의 WAL(Write Ahead Logging) 정책을 알아야 한다.
로그 먼저 쓰기, 모든 작업 전에 로그를 먼저 쓴다. 그리고 Redo log에 기록된 변경사항은 서버 시작 시점마다 비교하고 디스크에 반영한다.
리두 로그는 모든 트랜잭션 commit마다 디스크에 기록된다.
트랜잭션 커밋마다 리두로그 디스크 I/O가 발생한다는 것인데, 그럼 그냥 페이지를 쓰면 되지 않을까 의문이 들었다.
결론은 의미가 있다.
먼저 읽고 쓰는 데이터 크기 차이가 있다.
기본적으로 데이터가 저장되는 페이지는 16KB이지만, 리두 로그는 변경된 내용만 기록한다. 따라서 적은 양의 데이터를 읽고 쓰기 때문에 이점이 있다.
또 Redo 로그는 항상 디스크 헤드 이동 없이 끝에만 쓰는 순차 I/O이다.
언두로그에는 롤백, MVCC를 위한 데이터가 기록된다.
즉, 수정한 레코드를 이전 버전으로 복원할 수 있는 정보를 포함한다.
만약 트랜잭션이 롤백되거나, 읽기 작업 중 이전 버전을 읽어야 한다면 언두로그를 통해 데이터를 복원하는 과정을 거친다.
만약 언두로그 깊이가 깊다면 그만큼 복원을 위해 거쳐야 하는 작업이 많아진다. 이 점이 롱 트랜잭션이 성능 저하를 발생시키는 원인 중 하나이다.
변경 사항은 곧바로 디스크의 리두 로그로 가지 않는다. 먼저 메모리의 리두 로그 버퍼에 쌓인다.
그리고 트랜잭션이 커밋되는 시점에 이 버퍼를 디스크로 flush 한다. 이게 WAL이 보장하는 지점이다.
데이터 페이지보다 리두 로그가 먼저 디스크에 있어야 한다.
커밋마다 flush 하는 게 기본값이고, innodb_flush_log_at_trx_commit 설정으로 이 동작을 바꿀 수 있다. 0이나 2로 두면 커밋마다 디스크에 강제로 쓰지 않아 빠르지만, 장애 시 마지막 일부 트랜잭션을 잃을 수 있다.
각 변경에는 LSN(Log Sequence Number)이 붙는다. 로그가 어디까지 쌓였고, 디스크에 어디까지 반영됐는지를 가리키는 번호다.

커밋 시점에 디스크로 가는 건 리두 로그뿐이다. 실제 데이터 페이지는 그 자리에서 디스크에 쓰지 않는다.
수정된 페이지는 메모리에서 더티 페이지가 되고, 백그라운드에서 나중에 디스크로 내려간다. 즉, 커밋과 데이터 페이지 쓰기를 분리한 것이다.
이렇게 해도 되는 이유가 리두 로그다. 더티 페이지가 디스크에 못 내려간 채로 장애가 나도, 디스크에 남은 리두 로그를 재실행하면 복구된다.
흐름은 이렇다.
.ibd 파일에 쓴다페이지는 16KB지만 디스크는 보통 그보다 작은 단위로 쓴다. 그래서 페이지를 쓰는 도중 장애가 나면 페이지 일부만 기록되는 torn page(partial page write)가 생길 수 있다.
이렇게 깨진 페이지는 리두 로그로도 복구가 안 된다. 리두 로그는 "정상 페이지에 적용할 변경분"이지, 깨진 페이지를 통째로 되살리는 정보가 아니기 때문이다.
그래서 InnoDB는 더티 페이지를 .ibd 파일에 쓰기 전에, 먼저 Doublewrite Buffer라는 별도 공간에 통째로 한 번 쓴다. 그다음 원래 위치에 쓴다.
원래 위치에 쓰다 깨지면 Doublewrite Buffer에 남은 멀쩡한 사본으로 복구한다. 같은 데이터를 두 번 쓰지만, 순차 쓰기라 비용이 생각보다 크지 않다.