InnoDB 엔진의 버퍼 풀과 LRU에 대해서 공부하였다.
InnoDB 버퍼 풀
InnoDB 스토리지 엔진에서 가장 핵심적인 부분으로, 디스크의 데이터 파일이나 인덱스 정보를 메모리에 캐시해두는 공간이다.
쓰기 작업을 지연시켜 일괄 작업으로 처리해줄 수 있게 해주는 버퍼 역할도 같이 한다.
일반적인 INSERT, UPDATE, DELETE처럼 데이터를 변경하는 쿼리는 디스크 작업을 발생시키기 때문에,
버퍼 풀이 이러한 변경된 데이터를 한 번에 모아 디스크 작업의 횟수를 줄일 수 있다.
버퍼 풀의 크기 설정에 대한 내용은 MySQL 5.7부터 inno_buffer_pool_size 시스템변수를 통해 설정할 수 있으며, 동적으로 크기를 확장할 수 있다는 점을 짚고 넘어가겠다. (버퍼 풀의 크기를 동적으로 변경하고싶다면 반드시 MySQL 매뉴얼을 숙지할 것)
버퍼 풀의 구조
InnoDB 스토리지 엔진은 버퍼 풀이라는 거대한 메모리 공간을 페이지 크기(innodb_page_size 시스템 변수에 설정된) 조각으로 쪼개어 InnoDB 스토리지 엔진이 데이터를 필요로 할 때 해당 페이지를 읽어서 각 조각에 저장한다.
버퍼 풀의 페이지 조각들은 LRU 방식으로 관리된다.
더 자세하게는 LRU리스트와 MRU (Most Recently Used)리스트가 결합된 형태로 존재한다.
이렇게 리스트로 관리하는 목적은 당연히 디스크로부터 한번 읽어온 페이지를 최대한 오랫동안 버퍼 풀의 메모리에 유지함으로써, 디스크 읽기를 최소화하는데에 있다.
그림으로 보면 이렇게 생겨먹었다.
가운데 Midpoint 부분이 새로 들어온 페이지가 삽입되는 지점이다. 전체적으로는 LRU 알고리즘에 의해 전체 리스트의 Head쪽일수록 젊고, Tail쪽일수록 오래된 페이지들을 가리키게 된다.
LRU 알고리즘이 실제 어떤 흐름으로 버퍼 풀의 페이지들을 관리하는지 알아보자
- 버퍼 풀의 5/8은 new Sublist (상기한 MRU 리스트에 해당), 3/8은 LRU 리스트로 사용된다.
- InnoDB가 새로운 데이터 페이지를 버퍼 풀로 읽어오는 경우, midpoint 즉 old 리스트의 헤더에 삽입한다.
- old sublist에 존재하는 페이지를 접근할 경우, 해당 경우는 young한 페이지로 판단되어, MRU의 헤더로 이동한다.
- 위 과정은 새로운 데이터 페이지를 버퍼 풀로 읽어오는 경우와 다르게, 사용자 쿼리가 아닌 read-ahead에 의해 자동적으로 읽어오는 데이터는 해당하지 않는다.
- 데이터베이스 작업이 진행될수록, 버퍼 풀의 페이지들 중 자주 접근되지 않으면 자연스럽게 tail로 도태되는데, 이 과정은 데이터를 직접 tail로 옮기는 것이 아닌, young한 페이지들에 의해 밀려나는 것 뿐이다.
위 과정에서 아직 배우지 않은 read-ahead 작업이 무엇인지 궁금증을 품을 수 있을 것 같다.
또한 대량의 데이터가 한번에 버퍼 풀에 삽입되는 경우, 당연히 대량의 데이터가 밀려나며 버퍼 풀에서 삭제될 것이다.
read-ahead 작업과 table scan 쿼리는 버퍼 풀에 관해 조금 불편한 상황을 만드는 듯 싶은데, 이 현상에 대한 해결책은 기회가 된다면 공식문서를 읽으면 좋을 것 같다.
(자세히 보지는 않았지만 버퍼 풀 관련 최적화하는 방식이 있는 것 같다)
https://dev.mysql.com/doc/refman/8.0/en/innodb-performance-read_ahead.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-performance-midpoint_insertion.html
'Database' 카테고리의 다른 글
[MySQL] 인덱스와 잠금, MySQL의 격리 수준 (0) | 2025.01.10 |
---|---|
[MySQL] InnoDB 스토리지 엔진 잠금 (1) | 2025.01.07 |
[MySQL] MySQL 엔진의 잠금 (1) | 2025.01.04 |
[MySQL] InnoDB 엔진 (1) (1) | 2024.12.27 |
[MySQL] MyISAM ? InnoDB ? (1) | 2024.12.27 |