InnoDB 스토리지 엔진 아키텍처

InnoDB 스토리지 엔진은 현재 MySQL의 스토리지 엔진 중 가장 많이 사용된다. InnoDB 는 MySQL 에서 사용할 수 있는 스토리지 엔진 중 거의 유일하게 레코드 기반의 잠금을 제공하기 때문에 높은 동시성 처리가 가능하며 안정적이고 성능이 뛰어나다.
버퍼 풀 (Buffer Pool)
디스크의 데이터 파일이나 인덱스 정보를 메모리에 캐시해두는 메모리 공간이다. 쓰기 작업을 지연시켜 일괄 처리할 수 있도록 해준다.
일반 애플리케이션에서는 INSERT, UPDATE, DELETE 와 같은 DML이 데이터 파일의 이곳저곳에 위치한 레코드를 변경하기 때문에 랜덤한 디스크 작업을 발생시킨다. 이때 버퍼 풀이 이러한 변경된 데이터를 모아서 처리하여 랜덤한 디스크 작업의 횟수를 줄이는 것이다.
버퍼 풀의 구조
버퍼 풀은 거대한 메모리 공간이다. InnoDB 스토리지 엔진은 이 메모리 공간을 페이징 기법으로 관리하는데, 데이터가 필요할 때 해당 메모리 공간을 innodb_page_size 변수에 설정된 크기의 페이지 단위로 캐싱한다.
이 페이지들을 관리하기 위해 프리 리스트, LRU 리스트, 플러시 리스트 라는 세 가지 자료구조를 활용한다.
1. 프리 리스트
프리 리스트는 비어있는 페이지들의 목록을 관리한다. 프리 리스트는 InnoDB 버퍼 풀에서 실제 사용자 데이터로 채워지지 않은 페이지들의 목록이며, 사용자의 쿼리가 새롭게 디스크의 데이터 페이지를 읽어와야 하는 경우 사용된다.
사용자의 쿼리가 새롭게 디스크의 데이터 페이지를 읽어와야 하는 경우는 사용자가 요청한 데이터가 버퍼 풀에 없는 경우이다.
예를 들어 사용자가 특정 테이블의 데이터를 검색하는 쿼리를 실행하는 경우, InnoDB 스토리지 엔진은 먼저 버퍼 풀에서 해당 데이터 페이지가 있는지 검사한다. 만약 해당 데이터 페이지가 버퍼 풀에 없다면, 디스크에서 데이터 페이지를 읽어와서 버퍼 풀에 추가한다.
이때, 버퍼 풀에 새로운 데이터 페이지를 추가하기 위해 공간이 필요한 경우, 먼저 프리 리스트에서 비어있는 페이지를 찾는다.
2. LRU 리스트
버퍼 풀의 LRU 리스트는 LRU(Least Recently Used) 리스트와 MRU(Most Recently Used) 리스트가 결합된 형태의 리스트로 관리된다. 디스크로부터 한 번 읽어온 페이지를 최대한 오랫동안 InnoDB 버퍼 풀의 메모리에 유지해 디스크 읽기를 최소화하기 위해 사용된다.
다음 그림과 같은 구조이다.

버퍼 풀에 새로운 데이터 페이지를 추가하기 위해 공간이 필요하다면, 5/8 지점에 삽입이 이루어 진다(Midpoint insertion).
데이터 페이지를 읽으면 New Sublist 의 Head 쪽으로 이동하고, 사용하지 않는 데이터 페이지는 Old Sublist 의 Tail 쪽으로 이동하게 된다. 여기서 New Sublist 영역은 MRU 리스트, Old Sublist 영역은 LRU 리스트로 이해하면 된다.
이 리스트를 사용하면 디스크로부터 한 번 읽어온 페이지를 최대한 오랫동안 InnoDB 버퍼 풀의 메모리에 유지해 디스크 읽기를 최소화할 수 있다. InnoDB 스토리지 엔진에서 데이터를 찾는 과정을 한번 살펴보자.
1. 필요한 레코드가 저장된 데이터 페이지가 버퍼 풀에 있는지 검사한다.
2. 디스크에서 필요한 데이터 페이지를 버퍼 풀에 적재하고, 적재된 페이지에 대한 포인터를 LRU 헤더 부분에 추가한다.
3. 버퍼 풀의 LRU 헤더 부분에 적재된 데이터 페이지가 실제로 읽히면 MRU 헤더 부분으로 이동한다.
4. 버퍼 풀에 상주하는 데이터 페이지는 사용자 쿼리가 얼마나 최근에 접근했었는지에 따라 나이(Age) 가 부여되고, 버퍼 풀에 상주하는 동안 쿼리에서 오랫동안 사용되지 않으면 나이가 들어, 종국에는 버퍼 풀에서 제거(이 작업을 Eviction 이라 함)된다.
만약 쿼리에 의해 다시 사용되면, 나이가 초기화 되어 다시 젊어지고 MRU 헤더 부분으로 옮겨간다.
5. 필요한 데이터가 자주 접근됐다면, 해당 페이지의 인덱스 키를 어댑티브 해시 인덱스에 추가한다.
데이터 페이지가 얼마나 자주 사용되는지에 따라 해당 데이터 페이지가 MRU 영역에서 살아남는지, LRU 끝으로 밀려나 InnoDB 버퍼 풀에서 제거되는지가 결정된다.
3. 플러시 리스트
디스크와 동기화 되지 않은 변경 사항이 있는 데이터 페이지(더티 페이지)의 페이지 목록을 관리한다.
디스크에서 읽은 상태 그대로 변경이 없다면 플러시 리스트에서 관리되지 않고, 최소 한번 이상의 변경이 가해진 데이터 페이지는 플러시 리스트에서 관리되며 특정 시점에 디스크로 기록 되어야 한다. 즉, 쓰기 지연 효과를 누리기 위해 관리하는 목록이라 볼 수 있다.
로그 버퍼 (Logger Buffer)
로그 버퍼(Log Buffer)는 디스크의 로그 파일에 쓸 데이터를 저장해두는 메모리 공간이다.
MySQL 서버에서 InnoDB 스토리지 엔진은 데이터의 변경이 발생하면 디스크에 기록하기 전에 리두 로그(Redo Log) 와 언두 로그(Undo Log) 에 모두 기록한다. 이때 리두 로그를 ACID 를 보장하는 수준에서 버퍼링 하는 메모리 공간이 바로 로그 버퍼이다.
로그 버퍼의 크기는 innodb_log_buffer_size 시스템 변수에 정의되어 있다. 기본 크기는 16MB 이다.
로그 버퍼의 내용은 주기적으로 디스크에 플러시된다. 로그 버퍼의 메모리 크기를 크게 설정해 사용하면 트랜잭션을 커밋하기 전에 디스크에 리두 로그 데이터를 쓸 필요 없이 큰 트랜잭션을 실행할 수 있다.
따라서 많은 행을 업데이트, 삽입 또는 삭제하는 트랜잭션이 있는 경우 로그 버퍼의 크기를 늘리면 디스크 I/O가 절약된다. 예를 들어, BLOB 이나 TEXT 와 같이 큰 데이터를 자주 변경하는 경우가 될 수 있겠다.
[ 관련 글 ]
다음 글 - InnoDB 스토리지 엔진 아키텍처 : 체인지 버퍼
'개인 공부방' 카테고리의 다른 글
| [Real MySQL] 테이블 스페이스와 Double Write Buffer - InnoDB 스토리지 엔진 아키텍처 : On-Disk Structures (0) | 2023.08.03 |
|---|---|
| [Real MySQL] 어댑티브 해시 인덱스 - InnoDB 스토리지 엔진 아키텍처 : In-Memory Structures (0) | 2023.07.27 |
| [Real MySQL] 체인지 버퍼 - InnoDB 스토리지 엔진 아키텍처 : In-Memory Structures (0) | 2023.07.27 |
| [Real MySQL] MySQL 엔진 아키텍처 (1) | 2023.07.12 |
| [Real MySQL] MySQL 사용자 및 권한 관리하기 (0) | 2023.07.10 |