- InnoDB 버퍼 풀
InnoDB 스토리지 엔진에서 가장 핵심적인 부분으로, 디스크의 데이터 파일이나 인덱스 정보를 메모리에 캐시해 두는 공간이다. 쓰기 지연을 하는 버퍼 역할도 같이 한다.
버퍼 풀의 구조 - 크게 LRU 리스트, 플러시 리스트, 프리 리스트라는 3개의 자료 구조로 나뉜다.
- LRU 리스트 - LRU와 MRU 리스트가 결합된 형태
- LRU 리스트를 관리하는 목적은 디스크로부터 한 번 읽어온 페이지를 최대한 오랫동안 버퍼 풀의 메모리에 유지해서 디스크 읽기를 최소하하기 위해서다.
- 플러시 리스트 - 디스크로 동기화되지 않은 데이터를 가진 데이터 페이지의 변경 시점 기준의 페이지 목록을 관리
버퍼 풀의 데이터베이스 성능 향상 - 데이터 캐시와 쓰기 버퍼링
클린 페이지 - 디스크에서 읽은 상태로 전혀 변경되지 않음
더티 페이지 - 변경된 데이터를 가짐
더티 페이지는 디스크와 메모리(버퍼 풀)의 데이터 상태가 다르기 때문에 디스크로 기록돼야 함
버퍼 풀 플러시(더티 페이지 플러시) - 더티 페이지를 디스크로 동기화하는 것
더티 페이지들을 성능상의 악영향 없이 동기화하기 위해 다음 2개의 플러시 기능을 백그라운드로 실행
- 플러시 리스트 플러시
클리너 스레드 - 더티 페이지를 디스크로 동기화하는 스레드
클리너 스레드의 개수가 버퍼 풀 인스턴스 개수보다 적은 경우에는 하나의 클리너 스레드가 여러 개의 버퍼 풀 인스턴스를 처리하기 때문에 두 개수를 동일하게 맞추는 것이 좋다.
어댑티브 플러시 - 알고리즘을 이용해 적절한 수준의 더티 페이지가 버퍼 풀에 유지될 수 있도록 디스크 쓰기를 실행한다. - LRU 리스트 플러시
LRU 리스트에서 사용 빈도가 낮은 데이터 페이지들을 제거해서 새로운 페이지들을 읽어올 공간을 만든다.
버퍼 풀 상태 백업 및 복구
버퍼 풀은 쿼리의 성능에 매우 밀접하게 연결돼 있다. 서버를 셧다운했다가 다시 시작하는 경우 쿼리 처리 성능이 평상시보다 안좋은 경우가 대부분이다. 이는 버퍼 풀에 쿼리들이 사용할 데이터가 준비되어 있지 않기 때문이다. 그래서 서버를 다시 시작하는 경우 서비스를 오픈하기 전에 워밍업을 실시했었다. 하지만 MySQL 5.6 버전부터는 버퍼 풀 덤프 및 적재 기능이 도입돼서 편리하게 사용할 수 있다.
MySQL 서버가 셧다운되기 전에 버퍼 풀의 백업을 실행하고, MySQL 서버가 시작되면 자동으로 백업된 버퍼 풀의 상태를 복구할 수 있는 기능을 제공한다.
- 언두 로그
InnoDB 스토리지 엔진은 트랜잭션과 격리 수준을 보장하기 위해 DML(INSERT, UPDATE, DELETE)로 변경되기 이전 버전의 데이터를 별도로 백업한다. 이렇게 백업된 데이터를 언두 로그라고 한다.
언두 로그의 데이터 용도
1. 트랜잭션의 롤백 대비용
2. 트랜잭션의 격리 수준을 유지하면서 높은 동시성 제공
트랜잭션을 올바르게 완료하지 않고 방치하는 경우 디스크의 사용량이 증가하는 것 뿐 아니라 레코드를 조회할 때 필요한 만큼 언두 로그를 스캔해야하기 때문에 쿼리의 성능이 떨어질 수 있다.
- 체인지 버퍼
인덱스를 업데이트하려면 랜덤하게 디스크를 읽는 작업이 필요하므로 테이블에 인덱스가 많다면 이 작업은 상당히 많은 자원을 소모하게 된다. 그래서 InnoDB는 변경해야 할 인덱스 페이지가 버퍼 풀에 있으면 바로 업데이트를 수행하지만 그렇지 않고 디스크로부터 읽어와서 업데이트해야 한다면 이를 즉시 실행하지 않고 임시 공간에 저장해 두고 바로 사용자에게 결과를 반환하는 형태로 성능을 향상시키게 되는데, 이때 사용하는 임시 메모리 공간을 체인지 버퍼라고 한다.
유니크 인덱스는 반드시 중복 여부를 체크해야 하기 때문에 체인지 버퍼를 사용할 수 없다.
- 리두 로그
리두 로그는 하드웨어나 소프트웨어 등 여러 문제점으로 인해 MySQL 서버가 비정상적으로 종료됐을 때 데이터 파일에 기록되지 못한 데이터를 잃지 않게 해주는 안전장치다.
- 어댑티브 해시 인덱스
B-Tree 인덱스에서 특정값을 찾기 위해서는 루트 노드를 거쳐서 브랜치 노드, 리프 노드까지 찾아가야 하고, 이런 작업을 동시에 몇천 개의 스레드로 실행하면 CPU는 엄청난 프로세스 스케줄링을 하게 되고 쿼리의 성능은 떨어진다.
어댑티브 해시 인덱스는 이러한 B-Tree 검색 시간을 줄여주기 위해 도입된 기능이다.
사용자가 수동으로 생성하는 인덱스가 아니라 InnoDB 스토리지 엔진에서 사용자가 자주 요청하는 데이터에 대해 자동으로 생성하는 인덱스이다.
해시 인덱스는 '인덱스 키 값'과 해당 인덱스 키 값이 저장된 '데이터 페이지 주소'의 쌍으로 관리된다.
버퍼 풀에 올려진 데이터 페이지에 대해서만 관리되고, 버퍼 풀에서 해당 데이터 페이지가 없어지면 인덱스에서도 해당 페이지의 정보가 사라진다.
어댑티브 해시 인덱스가 성능 향상에 크게 도움이 되지 않는 경우도 있다. 데이터 페이지를 디스크에서 읽어오는 경우에는 아무런 도움이 되지 않는다.
어댑티브 해시 인덱스의 도움을 많이 받을수록 테이블 삭제 또는 변경 작업은 더 치명적인 작업이 될 수 있다. 어댑티브 해시 인덱스에서도 제거 또는 변경을 해줘야 하기 때문이다.
그래서 중요한 건 어댑티브 해시 인덱스가 우리 서비스 패턴에 맞게 도움이 되는지 아니면 불필요한 오버헤드만 만들고 있는지를 판단하는 것이다.
참조: 책 Real MySQL 8.0