1. 개요
리두로그와 언두로그를 공부하려했더니 Buffer Pool 이라는 개념이 등장했다. 테이블을 메모리에 캐싱해두는 개념인데, 단순 개념만 알고있기에는 중요한 내용이기에 정리하였다.
2. InnoDB Buffer Pool이 뭐에요?
말 그대로 InnoDB 엔진에서 제공하는 버퍼 풀인데, 이녀석의 역할은 바로 캐싱이다.
테이블이나 인덱스를 캐싱한다고 한다. 그럼 정확히 어떤 형태로 캐싱을 하고, 실제 환경에서 어떻게 사용되는지 알아보자.
3. Buffer Pool 은 '페이지'들이 캐싱되어 있어요
버퍼 풀에는 '페이지'들이 저장되어 있다. InnoDB 엔진에서의 페이지란 데이터베이스에서 데이터를 저장 단위이다. I/O를 통해 디스크로부터 페이지 단위로 데이터를 조회하면, Buffer Pool에 이를 그대로 저장한다. 페이지 하나의 크기는 16KB이다. (수정 가능하다)
페이지들은 버퍼 풀에 차곡차곡 쌓이는게 아니다. 버퍼 풀은 데이터를 효율적으로 캐싱하기 위해 LRU 리스트, Flush 리스트, Free 리스트 라는 3개의 리스트로 구성되어 있으며, 이 중 LRU 리스트에 어떤 규칙에 따라 저장되게 된다.
3.1. Free 리스트
비어있는 페이지들의 목록이다. 사용자의 쿼리가 디스크의 데이터 페이지를 읽어와야 하는 경우 사용된다.
3.2. Flush 리스트
디스크로 동기화되지 않은 데이터를 가진 데이터 페이지(== 더티 페이지)들의 목록이다. 즉, 디스크에서 데이터 페이지를 읽은 후 변경이 가해진 데이터 페이지들의 목록이다. 데이터가 변경되면 해당 페이지는 Flush 리스트에서 관리됨과 동시에 변경 내용을 리두 로그에 기록하고, 버퍼 풀에도 변경 내용을 반영한다. 그 후 특정 시점에 리두 로그가 디스크로 기록된다.
3.3. LRU (+MRU) 리스트
MySQL InnoDB 에서는 변형된 LRU 방식을 채택하고 있다. 바로 LRU와 MRU(Most Recently Used)가 결합된 하이브리드 방식이다. 참고로 LRU와 MRU는 캐시 알고리즘이다.
* LRU (Least Recently Used)
가장 최근에 사용되지 않은 페이지를 교체하는 알고리즘. 가장 오래된 캐시를 제거한다.
* MRU (Most Recently Used)
가장 최근에 사용된 페이지를 교체하는 알고리즘. 가장 최근에 사용된 캐시를 제거한다.
4. 가장 오래된 페이지와 가장 최근의 페이지를 제거한다고??
위 두 캐시 알고리즘이 버퍼 풀에 적용되어 있는 구조이다. 이해가 잘 되지 않는다면 어떻게 동작하는지에 대한 메커니즘을 그려나가보면 이해할 수 있을것이다.
아래는 변형된 LRU, 즉 LRU와 MRU가 결합된 형태의 리스트이다.
InnoDB에서의 페이지 캐시 메커니즘에 의해 New 서브리스트 자주 사용되는 페이지가 저장되는 영역, Old 서브리스트는 신규 혹은 자주 사용되지 않는 페이지가 저장되는 영역으로 사용될것이다. 편의상 New 서브 리스트가 존재하는 영역을 MRU 영역, Old 서브 리스트가 존재하는 영역을 LRU 영역이라 표현하겠다.
긴말 필요없이 바로 동작 메커니즘을 살펴보자
1) 필요한 레코드가 저장된 데이터 페이지가 버퍼 풀에 있는지 검사한다. [InnoDB 어댑티브 해시 인덱스 > 테이블 인덱스] 를 통해 페이지를 검색한다.
* 어댑티브 해시 인덱스
B-Tree 의 성능 개선을 위해 InnoDB 에서 자동으로 추가하는 인덱스이다. 자주 읽히는 데이터 페이지의 키값을 이용해 해시 인덱스를 만들고, 필요할 때마다 해당 데이터 페이지로 즉시 찾아갈 수 있게 한다.
- LRU/MRU를 설명하는 글이므로 자세히 다루지 않겠다.
2) 버퍼 풀에 데이터 페이지가 있다면 MRU 영역 방향으로 승급한다. (MRU 리스트로 들어가는게 아닌 방향으로 승급이다.)
3) 버퍼 풀에 데이터가 없다면 디스크로부터 필요한 데이터 페이지를 버퍼 풀에 적재와 동시에 LRU 영역 Head 부분에 추가한다.
4) LRU 영역에 추가한 데이터 페이지가 조회되면 MRU 영역 방향으로 승급하고, 버퍼풀에 없다면 LRU Head 부분에 추가하는 과정을 반복한다.
5) 이때, 승급하는 데이터 페이지가 LRU 영역의 Head에 위치했을 경우, 바로 위인 MRU 영역의 Tail이 아닌 MRU 영역의 Head로 이동한다. (이부분이 핵심이다!)
6) 1,2,3,4,5 과정이 반복되면서 자주 사용되지 않는 데이터 페이지들은 자연스럽게 LRU 영역의 Tail 쪽으로 밀려난다. 끝까지 밀려난 데이터 페이지들은 버퍼 풀에서 제거된다.
7) 자주 사용되는 데이터 페이지의 경우 성능 향상을 위해 페이지의 인덱스 키를 '어댑티브 해시 인덱스'에 추가한다.
5. LRU, 오래된 데이터가 제거되는 것은 이해가는데.. MRU는?
LRU 알고리즘이 적용됐다는 것은 이해가 갈것이다. 그럼 최근에 사용된 데이터가 먼저 삭제된다는 MRU는 대체 어디에 적용된 것일까? 데이터 레코드를 버퍼 풀로 읽어오는 과정을 직접 그려보며 '최근 사용된 데이터가 먼저 삭제'되는 MRU 부분을 이해해보자.
1) 1시 30분, 디스크로부터 A 데이터 페이지를 읽어 버퍼 풀에 적재한다. 최초 버퍼 풀 적재시에는 LRU 영역 Head 부분에 저장된다.
2) 1시 32분, 디스크로부터 B 데이터 페이지를 읽어 버퍼 풀에 적재한다. LRU 영역 Head 부분에 저장됨과 동시에 기존 A 데이터 페이지는 Tail 쪽으로 밀려난다.
3) 2시 0분, 디스크로부터 C 데이터 페이지를 읽어 버퍼 풀에 적재한다. LRU 영역 Head 부분에 저장됨과 동시에 A, B 데이터 페이지는 Tail 쪽으로 밀려난다.
4) 2시 10분, 드디어 C 데이터 페이지를 찾는 쿼리가 실행됐고, C 데이터 페이지를 버퍼 풀에서 로드하게 된다. 이때 C 데이터 페이지는 LRU 영역의 Head에 위치했기 때문에 MRU 영역의 Head로 이동하게 된다.
5) 2시 20분, 디스크로부터 D 데이터 페이지를 읽어 버퍼 풀에 적재한다. LRU 영역 Head 부분에 저장됨과 동시에 A, B 데이터 페이지는 Tail 쪽으로 밀려난다.
6) 계속해서 신규 쿼리가 들어옴에 따라 디스크로부터 데이터 페이지를 읽어오게 되었다. E, F, G... X, Y, Z 까지, 다양한 데이터 페이지를 디스크로부터 로드하다보니 5시 30분이 되었다.
6. 최신의 데이터가 먼저 제거된다!
2시 10분에 저장한 C 데이터 페이지보다 최근에 사용했었던 E, F, G, H... 들의 데이터 페이지가 버퍼 풀에서 먼저 제거되었다. MRU 영역에서도 자주 사용되지 않은 데이터 페이지들은 Tail 쪽으로 밀려난다. 단, MRU 영역에 있는 C 데이터 페이지가 삭제되지 않은 이유는, 다른 데이터 페이지들이 여러번 재사용되지 않았고, 이로 인해 MRU 영역에서의 '경쟁'이 발생하지 않았기 때문이다.
'최근 사용된 데이터인 E,F,G,H 등의 데이터 페이지들이 먼저 삭제'된다는 점에서 MRU 알고리즘이 적용되었음을 알 수 있다.
7. 정리하면... InnoDB의 LRU + MRU 버퍼 풀
InnoDB 버퍼 풀은 하이브리드 방식을 사용함으로써 단순 오래된 데이터 페이지 뿐 아니라, 자주 사용되지 않은 최신의 데이터 페이지들도 제거하고 있다. 만약 MRU 알고리즘이 적용되지 않았다면 어떨까? 최근 액세스한 데이터 페이지 들 중 자주 사용되지 않아 사실상 필요없는 데이터 페이지가 메모리에서 공간만 차지할것이고, 버퍼 풀이 가득 차서야 제거될것이다. 캐시 메모리를 최대한 활용하기 위해서는 자주 사용하는 데이터를 많이 저장하고, 최신의 데이터라할지라도 자주 사용하지 않은 데이터는 제거하는 것이 좋지 않겠는가?!
오늘은 InnoDB Buffer Pool에 대한 기본 구조와 InnoDB의 LRU + MRU 리스트에 대해 알아보았다. 다음은 리두 로그와 언두 로그에 대해 공부해보도록 하겠다!
'DB > MySQL' 카테고리의 다른 글
[MySQL] 비관적 잠금과 낙관적 잠금 (0) | 2025.01.14 |
---|---|
[MySQL] MySQL 엔진 / 실행 구조 / 버퍼 (0) | 2024.10.16 |