본문 바로가기
ComputerScience/RealMySQL

4.MySQL 아키텍처 - 4.2 InnoDB 스토리지 엔진 아키텍처(2)

by 규난 2022. 12. 1.
728x90

2022.11.28 - [RealMySQL] - 4.MySQL 아키텍처 - 4.2 InnoDB 스토리지 엔진 아키텍처(1)

 

4.MySQL 아키텍처 - 4.2 InnoDB 스토리지 엔진 아키텍처(1)

이번 포스트에서는 MySQL의 스토리지 엔진 중 하나인 InnoDB 스토리지 엔진 아키텍처에 대해서 포스트하려합니다. InnoDB는 MySQL에서 사용할 수 있는 스토리지 엔진 중 거의 유일하게 레코드 기반의

rbsks.tistory.com

전 포스트에 이어서 MySQL InnoDB 스토리지 엔진 아키텍처에 대해서 더 알아보려고 합니다.

 

InnoDB 버퍼 풀

InnoDB 스토리지 엔진에서 가장 핵심적인 부분으로, 디스크의 데이터 파일이나 인덱스 정보를 메모리에 캐시해 두는 공간이면서 쓰기 작업을 지연시켜 일괄 작업으로 처리할 수 있게 해주는 버퍼 역할을 합니다.

 

애플리케이션의 INSERT, UPDATE, DELETE 처럼 데이터를 변경하는 쿼리는 데이터 파일의 이곳저곳에 위치한 레코드를 변경하기 때문에 랜덤한 디스크 작업을 발생시켜 효율성이 떨어지게되는데 InnoDB는 이러한 변경 데이터를 모아서 처리하기 때문에 랜덤한 디스크 작업 횟수를 줄일 수 있습니다.

 

MySQL5.7 버전부터는 innodb_buffer_pool_size 시스템 변수로 InnoDB 버퍼 풀의 크기를 동적으로 조절 할 수 있게 되었습니다.

하지만 버퍼 풀 크기를 함부로 변경하는 것은 매우 위험하므로 MySQL 서버가 한가한 시점에 골라서 진행하는것이 좋습니다.

 

버퍼 풀의 구조

InnoDB 스토리지 엔진은 버퍼 풀이라는 거대한 메모리 공간을 페이지 크기(innodb_page_size 시스템 변수로 설정)의 조각으로 쪼개어 

스토리지 엔진이 데이터를 필요로 할 때 해당 데이터 페이지를 읽어서 쪼갠 조각에 저장합니다.

 

버퍼 풀의 페이지 크기 조각을 관리하기 위해 크게 LRU(Least Recently Used) 리스트, 플러시(Flush) 리스트, 프리(Free) 리스트라는 3개의 자료 구조를 관리합니다.

 

프리 리스트는 버퍼 풀에서 실제 사용자 데이터로 채워지지 않은 비어있는 페이지들의 목록이며, 사용자의 쿼리가 새롭게 디스크의 데이터 페이지를 읽어와야 하는 경우 사용됩니다.

 

LRU 리스트는 엄밀하게 LRU와 MRU(Most Recently Used) 리스트가 결합된 형태라고 보면 됩니다.

밑 그림에서 Old 서브리스트는 LRU 영역, New 서브리스트는 MRU 영역으로 이해하시면 됩니다.

LRU 리스트를 관리하는 목적은 디스크로부터 한 번 읽어온 페이지를 최대한 오랫동안 버퍼 풀의 메모리에 유지해서 디스크 읽기를 최소화하는 것입니다.

 

처음 한 번 읽힌 데이터 페이지가 이후 자주 사용된다면 그 데이터 페이지는 버퍼 풀의 MRU 영역으로, 반대로 거의 사용되지 않는다면 새롭게 읽히는 데이터 페이지들에 밀려서 LRU 영역 끝으로 밀려나 결국 버퍼 풀에서 제거됩니다.

버퍼 풀 관리를 위한 LRU 리스트 구조

플러시 리스트는 디스크로 동기화되지 않은 데이터를 가진 데이터 페이지(더티 페이지)의 변경 시점 기준의 페이지를 관리합니다.

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

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

 

버퍼 풀과 리두 로그

InnoDB 버퍼 풀은 데이터베이스 서버의 성능 향상을 위해 데이터 캐시와 쓰기 버퍼링이라는 두 가지 기능을 사용합니다.

버퍼 풀의 메모리 공간만 늘리는 것은 데이터 캐시 기능만 향상시키고, 쓰기 버퍼링 기능까지 향상 시키려면 리두 로그 까지 같이 관리를 해줘야 합니다.

 

버퍼 풀은 디스크에서 읽은 상태로 전혀 변경되지 않은 클린 페이지(Clean Page)와 INSERT, UPDATE, DELETE 명령으로 변경된 데이터를 가진 더티 페이지(Dirty Page)가 있습니다.

 

리두 로그는 1개 이상의 고정 크기 파일을 연결해서 순환 고리처럼 사용이 돼서 데이터 변경이 계속 발생하면 리두 로그 파일에 기록됐던 로그 엔트리는 결국 새로운 로그 엔트리로 덮어쓰이게 됩니다. 그래서 전체 리두 로그 파일에서 재사용 가능한 공간과 불가능한 공간을 구분해서 관리해야하는데, 재사용 불가능한 공간을 활설 리두 로그(Active Redo Log)라고 합니다.

 

리두 로그 파일의 공간은 순환되어 재사용되지만 매번 기록될 때 마다 로그 포지션은 계속 증가된 값을 갖게 되는데 이를 LSN(Log Sequence Number)이라고 합니다. InnoDB 스로리지 엔진은 주기적으로 체크포인트 이벤트를 발생시켜 리두 로그와 버퍼 풀의 더티 페이지를 디스크로 동기화 하는 작업을 하는데, 가장 최근 체크포인트 지점의 LSN과 마지막 리두 로그 엔트리의 LSN(체크 포인트라 함) 만큼 리두 로그와 더티 페이지를 디스크로 동기화 합니다.

 

Double Wrtie Buffer

InnoDB 스토리지 엔진의 리두 로그는 리두 로그 공간의 낭비를 막기 위해 페이지의 변경된 내용만 기록합니다.

이로 인해 버퍼 풀의 더티 페이지를 디스크 파일로 플러시 할 때 일부만 기록되는 문제가 발생하면 그 페이지의 내용은 복구할 수 없을 수도 있습니다. 이렇게 페이지가 일부만 기록되는 현상을 파셜 페이지(Partial Page) 또는 톤 페이지(Torn Pag)라고 하는데, 이런 현상은 하드웨어의 오작동이나 시스템의 비정상적인 종료로 발생할 수 있습니다.

 

이러한 문제를 막기 위해 Double Write 기법이 등장하였습니다.

실제 데이터 파일에 변경 내용을 기록하기 전에 더티 페이지를 묶어서 한번에  시스템 테이블스페이스의 Double Write 버퍼에 기록합니다.

그 후 InnoDB 스토리지 엔진은 더티 페이지를 디스크로 동기화 하게 되는데 정상적으로 디스크로 동기화가 됐으면 Double Write 버퍼에 기록한 데이터들은 필요가 없어지게 되고, 만약 디스크로 동기화 작업 중 중간에 실패하게 되면 Double Write 버퍼의 데이터와 디스크의 데이터 파일의 데이터를 비교해서 서로 다른 내용을 담고 있으면 Double Write 버퍼의 내용을 디스크의 데이터 파일에 복사하게 됩니다.

이 기능을 사용할지 여부는 innodb_doublewrite 시스템 변수로 결정할 수 있습니다.

 

728x90