InnoDB 알아보기 (In-Memory Structures - Buffer Pool)

2024. 1. 29. 23:55Database

Buffer Pool

디스크의 데이터 파일, 인덱스 정보를 메모리에 캐시해 두는 공간이다.

쓰기 작업을 버퍼링하여 일괄적으로 처리할 수 있게 해주는 버퍼 역할도 수행한다.

 

 

 

✔️ 버퍼 풀 크기 설정

공식 문서에 따르면 일반적으로 전체 물리 메모리의 80% 정도를 버퍼 풀에 할당한다.

Real MySQL 에서는 다음과 같이 버퍼 풀의 크기를 설정할 것을 권장한다.
- 운영체제의 전체 메모리 공간이 8GB 미만인 경우 50% 정도만 버퍼 풀로 설정한다.
- 그 이상인 경우 전체 메모리의 50%부터 시작해서 조금씩 올려가며 최적의 값을 찾는다.
- 전체 메모리 공간이 50GB 이상이라면 20~ 35GB 정도 설정한다.

 

버퍼 풀의 크기는 innodb_buffer_pool_size를 통해 동적으로 변경할 수 있다.

크기를 늘리는 것은 괜찮으나, 줄이는 경우 시스템에 매우 큰 영향을 주기 때문에 주의해야 한다.

 

버퍼 풀은 내부적으로 128MB 크기의 청크 단위로 쪼개어 관리되며,

이는 크기를 늘리거나 줄이기 위한 단위 크기로 사용된다.

(즉 버퍼 풀의 크기 변경은 128MB 단위로 처리되며, 더욱 자세한 내용은 공식문서를 참고하자.)

 

하나의 버퍼 풀은 여러 개의 작언 버퍼 풀로 분리하여 관리할 수 있는데,

이때 분리된 각 버퍼 풀을 버퍼 풀 인스턴스라고 한다.

 

버퍼 풀 인스턴스의 개수는 Innodb_buffer_pool_instances 를 통해 설정할 수 있으며 기본값은 다음과 같다.

  • 기본적으로 8개
  • 전체 버퍼 풀을 위한 메모리 크기가 1GB 미만이면 1개
버퍼 풀의 메모리 공간이 40GB 이하라면 기본값(8)을 유지하고,
그 이상이라면 버퍼 풀 인스턴스당 5GB 정도가 되도록 설정하는 것을 권장한다.

 

 

 

 

 

 

LRU / Flush / Free List

InnoDB는 버퍼 풀을 페이지 크기(innodb_page_size)로 쪼개서 각 조각에 데이터를 저장한다.

이러한 페이지 조각을 관리하기 위해 LRU(Least Recently Used) / Flush / Free 리스트 자료구조를 사용한다.

 

 

 

✔️ LRU List

LRU 리스트 구조

 

LRU List는 LRU 와 MRU(Most Recently Used) 리스트가 결합된 형태이다.

New Sublist가 LRU, New 서브리스트가 MRU 영역이다.

 

LRU 알고리즘에서 자주 사용되는 페이지는 New Sublist에 존재한다.

Old Sublist에는 자주 사용되지 않는 페이지들이 존재하며, 이들이 제거(Eviction) 후보가 된다.

 

 

LRU List의 알고리즘은 다음과 같다.

  • 버퍼 풀의 3/8 은 old sublist에 할당된다.
  • midpoint는 old sublist의 head와 new sublist의 tail이 만나는 경계이다.
  • InnoDB는 페이지를 디스크로부터 버퍼 풀로 읽어올 때, midpoint에 페이지를 삽입한다.
    • 페이지가 읽히는 이유는 사용자의 요청이나 InnoDB가 자동으로 실행하는 Read-Ahead 때문이다.
  • Old sublist에 존재하는 페이지에 접근하게 되면, 해당 페이지는 young 상태가 되며 new sublist의 head 로 이동한다.
  • 위 작업이 반복됨에 따라, 자주 사용되지 않는 페이지들은 old sublist로 이동하게 되고 tail에 도착하면 제거된다.
  • 필요한 데이터가 자주 접근되는 경우, 해당 페이지의 인덱스 키를 어댑티브 해시 인덱스에 추가한다.

 

 

 

 

✔️ Flush List

디스크로 동기화되지 않은 데이터를 가진 데이터 페이지(= dirty page)의 변경 시점 기준의 페이지 목록을 관리한다.

디스크에서 읽은 상태에서 변경되지 않은 경우 플러시 리스트에 관리되지 않지만,

변경이 가해진 데이터 페이지는 플러시 리스트에 관리되고 특정 시점이 되면 디스크로 기록돼야 한다.

 

데이터가 변경되면 InnoDB는 변경 내용을 리두 로그에 기록하고, 버퍼 풀의 데이터 페이지에도 변경 내용을 반영한다.

그러나 리두 로그가 디스크로 기록되었다고 해서, 데이터 페이지가 디스크로 기록되는 것을 보장하지는 못한다. (반대의 경우도 마찬가지이다.)

따라서 이 둘을 동기화하는 작업이 필요하고,

InnoDB는 체크포인트를 발생시켜 디스크의 리두 로그 데이터 페이지의 상태를 동기화한다.

 

 

 

 

✔️ Free List

버퍼 풀에 존재하는 비어 있는 페이지들의 목록을 관리한다.

사용자의 쿼리로 인해 새롭게 디스크의 데이터 페이지를 읽어와야 하는 경우 사용된다.

 

 

 

 

 

 

버퍼 풀 플러시

버퍼 풀에 존재하는 더티 페이지들을 디스크에 동기화하는 작업을 버퍼 풀 플러시라 한다.

버퍼 풀 플러싱은 페이지 클리너 스레드(page cleaner thread)에 의해 백그라운드로 수행된다.

 

버퍼 풀 플러시 실행 시 너무 많은 더티 페이지를 디스크로 기록하게 되면 사용자의 쿼리 처리에 악영향을 미칠 수 있다.

 

이를 방지하기 위해 다음과 같은 설정들을 적당히 튜닝하여 사용해야 한다.

  • innodb_page_cleaners
    • 페이지 클리너 스레드의 수를 설정한다.
    • 기본값은 4이지만 클리너 스레드의 수가 버퍼 풀 인스턴스의 수(innodb_buffer_pool_instances)를 초과하는 경우 버퍼 풀 인스턴스와 같은 수로 자동 설정된다.
  • innodb_max_dirty_pages_pct_lwm
    • 더티 페이지의 비율이 해당 설정 값에 도달하면 버퍼 풀 플러시가 시작된다.
    • 기본값은 버퍼 풀의 10%이며, 0으로 설정된 경우 비활성화된다.
    • 더티 페이지의 비율이 innodb_max_dirty_pages_pct에 도달하는 것을 방지하기 위해 설정한다.
    • 반드시 innodb_max_dirty_pages_pct 보다 낮게 설정해야 한다.
  • innodb_max_dirty_pages_pct
    • 버퍼 풀의 더티 페이지의 비율이 해당 값이 도달하면 곧바로 플러시한다.
    • 이로 인해 플러시가 발생하는 경우 디스크 쓰기 폭발(Disk IO Burst)이 발생할 가능성이 높아진다.
  • innodb_io_capacity
    • 버퍼 풀에서 페이지를 플러시하는 등 InnoDB 백그라운드 작업에서 사용할 수 있는 초당 입출력 작업 수(IOPS)를 정의한다.
  • innodb_io_capacity_max
    • 플러시 작업이 뒤쳐지는 경우 innodb_io_capacity에 설정된 값보다 더 높은 속도로 플러싱할 수 있는데, 이러한 경우 InnoDB 백그라운드 작업에서 사용할 수 있는 초당 입출력 작업 수(IOPS)를 정의한다.

 

lwm은 Low Water Mark의 축약어이다.

 

 

 

 

✔️ Adaptive flush

MySQL 서버의 트래픽을 일일이 분석하여 innodb_io_capacity, innodb_io_capacity_max 값을 설정하는 작업은 번거롭다.

이를 위해 어댑티브 플러시라는 기능을 제공한다. 

 

어댑티브 플러시를 사용하면 버퍼 풀의 페이지 비율이나 innodb_io_capacity, innodb_io_capacity_max 설정에 의존하지 않고 새로운 알고리즘을 사용한다.

 

더티 페이지를 얼마나 디스크로 기록해야 될 지 분석하는 것은 더티 페이지의 생성 속도를 분석하는 것과 같다.

더티 페이지는 데이터의 변경에 의해 생성되므로, 더티 페이지의 생성 속도는 결국 리두 로그의 증가 속도를 분석하여 알 수 있다.

 

어댑티브 플러시는 리두 로그의 증가 속도를 분석하여,

버퍼 풀에 적정량의 더티 페이지가 유지될 수 있도록 디스크 쓰기를 실행하는 알고리즘이다.

 

관련된 설정은 다음과 같다.

  • innodb_adaptive_flushing
    • 어댑티브 플러시 기능의 활성화 여부를 설정한다. 기본값은 활성이다.
  • innodb_adaptive_flushing_lwm
    • 전체 리두 로그 공간에서 활성 리두 로그 공간이 해당 설정값 미만이면 어댑티브 플러시가 작동하지 않는다.
    • 기본값은 10%이다.

 

 

 

✔️ LRU 리스트 플러시

InnoDB는 LRU 리스트에서 사용 빈도가 낮은 데이터 페이지들을 제거하여 새로운 페이지들을 읽어올 공간을 만들어야 한다.

이때 LRU 리스트 플러시를 사용한다.

 

관련 설정은 다음과 같다.

  • innodb_lru_scan_depth
    • 버퍼 풀 인스턴스별로 page cleaner thread가 플러시할 더티 페이지를 찾기 위해 LRU(Old sublist) 목록의 맨 아래(tail)부터 몇 개의 페이지를 스캔할지 설정한다.
    • 버퍼 풀 인스턴스별로 스캔되기 때문에, 실질적으로 LRU 리스트의 스캔은 (innodb_buffer_pool_instances * innodb_lru_scan_depth) 수만큼 수행된다.

LRU 리스트 플러시는 페이지 클리너 스레드가 초당 한 번 백그라운드로 수행한다.

 

 

 

 

 

 

참고