forDevLife
InnoDB 버퍼 풀과 주변 기능 본문
InnoDB 스토리지 엔진에서 가장 핵심적인 부분이며, 크게 아래의 기능을 하게 된다.
- 디스크의 데이터 파일이나 인덱스 정보를 메모리에 캐시해둔다.
- 쓰기 작업을 지연시켜 일괄 처리가 가능하도록 해주는 버퍼 역할도 한다.
버퍼 풀의 구조
InnoDB 스토리지 엔진은 버퍼 풀이라는 거대한 메모리 공간을 페이지 크기의 조각(기본 16KB)으로 쪼개어, 필요할 때 해당 데이터 페이지를 디스크로부터 읽어서 각 조각에 저장한다. 이러한 페이지 조각들을 관리하기 위해 크게 3가지의 자료구조를 사용한다.
- 프리 리스트
: InnoDB 버퍼 풀에서 실제 사용자 데이터로 채워지지 않은 비어 있는 페이지의 목록이다. - LRU 리스트
: 디스크로부터 한 번 읽어온 페이지를 최대한 오랫동안 InnoDB 버퍼 풀의 메모리에 유지하기 위한 자료구조이다. 읽힌 데이터 페이지의 사용 빈도에 따라 LRU 리스트 내에서 이동하며, 맨 끝으로 밀려날 경우 LRU 리스트 포인터가 가리키는 버퍼 풀의 데이터 페이지를 비워내 다시 사용 가능해지도록 프리 리스트로 전달하고 LRU 리스트에서는 삭제한다. - 플러시 리스트
: 디스크로 동기화하지 않은 데이터를 가진 데이터 페이지를 더티 페이지라고 하며, 이를 관리하는 리스트이다. 데이터가 변경된다면 InnoDB는 변경 내용을 리두 로그에 기록하고 버퍼 풀의 데이터 페이지에도 내용을 반영한다.
버퍼 풀과 리두 로그
InnoDB의 버퍼 풀과 리두 로그는 매우 밀접한 관계를 가지고 있다.
앞서 설명했듯이 버퍼 풀은 데이터 캐시와 쓰기 버퍼링이라는 두 가지 용도가 있는데, 다시 살펴보면 다음과 같다.
- 디스크의 데이터 파일이나 인덱스 정보를 메모리에 캐시해둔다. -> 버퍼 풀의 메모리 공간을 늘리면 성능이 향상된다.
- 쓰기 작업을 지연시켜 일괄 처리가 가능하도록 해주는 버퍼 역할도 한다. -> 리두 로그를 활용해야 한다.
이를 위해서 버퍼 풀과 리두 로그의 관계를 우선 살펴볼 필요가 있다.
- 버퍼 풀은 전혀 변경되지 않은 클린 페이지와 INSERT, UPDATE, DELETE로 변경된 데이터를 가진 더티 페이지로 이루어진다.
- 더티 페이지는 디스크와 데이터 상태가 다르기 때문에 언젠가는 디스크로 기록되어야 한다.
- 이 때 리두 로그는 1개 이상의 고정 크기 파일을 연결해서 순환 고리처럼 사용한다.
- 데이터 변경이 계속 발생되면 리두 로그가 덮어씌워질 가능성이 있게 된다.
- 이 때, 위 그림에서 화살표를 가진 엔트리들을 "활성 리두 로그 공간"이라고 한다.
- 이 공간은 재사용 불가능한 공간이다.
- 리두 로그 파일 공간은 순환되며 재사용되지만 매번 기록될 때마다 증가된 값을 갖는다.
이를 LSN(Log Sequence Number)라고 한다. - InnoDB 스토리지 엔진은 주기적으로 체크포인트 이벤트를 발생시켜 리두 로그와 버퍼 풀의 더티 페이지를 디스크로 동기화하는데, 이렇게 발생한 체크포인트 중 가장 최근 체크포인트 지점의 LSN이 활성 리두 로그 공간의 시작점이 된다.
- 마지막 리두 공간의 LSN과 가장 최근 LSN의 차이를 Checkpoint Age라 하며, 이는 활성 리두 공간의 크기가 된다.
체크포인트가 발생하면 체크포인트 LSN보다 작은 리두 로그 엔트리에 연결된 더티 페이지들은 모두 디스크로 동기화돼야 한다. 이 때 리두 로그도 디스크로 동기화된다.
버퍼 풀 플러시(Buffer Pool Flush)
InnoDB 스토리지 엔진은 버퍼 풀에서 아직 디스크로 기록되지 않은 더티 페이지들을 성능상의 악영향 없이 디스크에 동기화하기 위해 다음 2개의 플러시 기능을 백그라운드로 실행한다. 앞서 페이지를 관리하기 위한 리스트를 대상으로 하는 플러시 방법이다.
- 플러시 리스트 플러시 : 플러시 리스트에서 오래전에 변경된 데이터 페이지 순서대로 디스크에 동기화하는 작업을 수행한다.
- LRU 리스트 플러시 : LRU 리스트에서 사용 빈도가 낮은 데이터 페이지를 제거하는데, 이 때 끝부분(OLD)에서부터 스캔하며 더티 페이지를 디스크에 동기화하는 작업이다.
버퍼 풀 상태 백업 및 복구
디스크의 데이터가 버퍼 풀에 적재돼 있는 상태를 워밍업(Warming Up)이라고 표현하는데, 이게 잘 되어 있을 경우 몇십 배의 쿼리 처리 속도를 보이는 것이 일반적이다. 기존에는 워밍업 작업을 위해 주요 테이블과 인덱스를 풀 스캔하는 '강제 워밍업'을 사용했다.
MySQL 5.6 버전부터는 버퍼 풀 덤프 및 적재 기능이 도입되었다. 다음 환경 변수를 통해 현재 InnoDB 버퍼 풀의 상태를 백업하고 복구할 수 있다.
-- // MySQL 서버 셧다운 전에 버퍼 풀의 상태 백업
mysql> SET GLOBAL innodb_buffer_pool_dump_now=ON;
-- // MySQL 서버 재시작 후, 백업된 버퍼 풀의 상태 복구
mysql> SET GLOBAL innodb_buffer_pool_load_now=ON;
버퍼 풀의 백업은 데이터 디렉터리에 ib_buffer_pool이라는 이름으로 생성되며, LRU 리스트에서 적재된 데이터 페이지의 메타 정보만 가져와서 저장하기 때문에 용량이 작고 매우 빨리 완료된다.
하지만 백업된 버퍼 풀을 다시 복구하는 과정은 백업 메타 정보를 통해 디스크에서 읽어오는 과정이므로 시간도 많이 소요되고 크기도 상당하다. 따라서 이 과정을 확인할 수 있는 상태 값을 제공한다.
mysql> SHOW STATUS like 'Innodb_buffer_pool_dump_status'\G
Double Write Buffer
InnoDB 스토리지 엔진의 리두 로그는 공간 효율성을 위해 페이지의 변경된 내용만을 기록한다. 따라서 더티 페이지를 디스크 파일로 플러시 하는 과정에서 에러가 발생해 일부만 기록되는 문제가 발생한다면 나머지 부분의 내용은 복구할 수 없을 수도 있다.
이와 같은 문제를 막기 위해 Double-Write 기법을 이용한다. 더티 페이지(1 ~ 5)를 디스크로 플러시하는 상황에서 에러발생을 가정하자.
- 실제 데이터 파일에 변경 내용을 기록하기 전에 1~5까지의 더티 페이지를 우선 묶어서 한 번의 디스크 쓰기로 시스템 테이블 스페이스의 DoubleWrite 버퍼에 기록한다. 즉 더티 페이지를 별도의 공간에 백업한다고 생각하면 된다.
- 버퍼 풀에 있는 더티 페이지를 하나씩 디스크에 쓴다.
- 쓰는 도중 비정상적으로 종료가 되었다.
- 재시작 시 DoubleWrite 버퍼의 내용과 데이터 파일의 페이지들을 모두 비교한다. 다른 내용을 담고 있는 페이지가 있다면 DoubleWrite 버퍼의 내용을 데이터 파일로 복사한다.
Double Write Buffer의 사용 여부는 innodb_doublewrite 시스템 변수로 제어할 수 있다.
HDD와 같이 순차 쓰기에 부담이 없을 경우에는 괜찮지만, SSD의 경우에는 이렇게 한 번에 백업 데이터를 쓰는 과정이 부담스러울 수 있다. 하지만 데이터의 무결성이 매우 중요한 서비스에서는 Double Write Buffer의 활성화를 고려하는 것이 좋다.
'Database' 카테고리의 다른 글
트랜잭션과 잠금 (0) | 2022.05.24 |
---|---|
InnoDB 스토리지 엔진 아키텍처 2 (0) | 2022.05.17 |
InnoDB 스토리지 엔진 아키텍처 1 (0) | 2022.05.12 |
스레드 캐시, Connection Pool, Thread Pool (0) | 2022.05.10 |
MySQL 아키텍처 (0) | 2022.05.06 |