[Real MySQL 8.0] MySQL 엔진 아키텍처
MySQL 서버는 크게 머리 역할을 담당하는 MySQL 엔진과 손발 역할을 담당하는 스토리지 엔진으로 구성된다. 기본으로 제공되는 스토리지 엔진에는 InnoDB와 MyISAM 등이 있다.
MySQL의 구조
MySQL 엔진
MySQL 엔진은 다음과 같은 구성요소가 중심을 이룬다.
MySQL 엔진은 다음과 같은 구성 요소들이 중심을 이룬다.
- 접속 및 쿼리 요청을 처리하는 커넥션 핸들러
- SQL 파서
- 전처리기
- 옵티마이저
또한, ANSI SQL 문법을 지원하기 때문에 다른 DBMS와 호환되어 실행될 수 있다.
MySQL 엔진은 SQL 문장을 분석하거나 최적화하는 등 DBMS의 두뇌 역할을 맡고 있다고 보면 된다. 우리의 머리가 하나인 것처럼 MySQL 서버에서도 MySQL 엔진은 하나만 존재한다.
스토리지 엔진
스토리지 엔진은 실제 데이터를 디스크 스토리지에 저장하거나 읽어오는 부분을 담당한다.
MySQL 엔진과 다르게 스토리지 엔진은 여러 개를 동시에 사용할 수 있다.
핸들러 API
MySQL 엔진의 쿼리 실행기에서 스토리지 엔진에 쓰기 또는 읽기를 요청하는데, 이를 핸들러 요청이라고 한다. 이때 사용되는 API를 핸들러 API라고 부른다. InnoDB 스토리지 엔진 또한 핸들러 API를 이용해 MySQL 엔진과 데이터를 주고받는다.
MySQL 스레딩 구조
MySQL 서버는 프로세스 기반이 아닌 스레드 기반으로 동작하며, 크게 포그라운드 스레드와 백그라운드 스레드로 구분할 수 있다.
포그라운드 스레드
포그라운드 스레드는 MySQL 서버에 접속된 클라이언트 수만큼 존재하며, 주로 사용자가 요청하는 쿼리 문장을 처리한다. 그 후 사용자가 작업을 마치고 커넥션을 종료하면 해당 스레드는 다시 스레드 캐시로 되돌아간다. 만약 스레드 캐시에 이미 일정 개수 이상의 스레드가 대기 중이라면 해당 스레드를 종료시켜 일정 개수의 스레드만 스래드 캐시에 존재하게 한다.
포그라운드 스레드는 데이터를 MySQL의 데이터 버퍼나 캐시로부터 가져오며, 버퍼나 캐시에 없는 경우 직접 디스크의 데이터나 인덱스 파일로부터 데이터를 읽어와 작업을 처리한다.
백그라운드 스레드
InnoDB 스토리지 엔진의 백그라운드 스레드에서 가장 중요한 것은 로그 스레드와 버퍼의 데이터를 디스크로 쓰기 작업을 처리하는 쓰기 스레드이다. 쓰기 스레드는 아주 많은 작업들을 백그라운드로 처리하기 때문에 디스크를 최적으로 사용할 수 있을 만큼 충분히 설정하는 것이 좋다.
일반적인 상용 DMBS에서는 쓰기 작업을 버퍼링해서 일괄 처리하는 기능이 탑재되어 있고 InnoDB 또한 이러한 방식으로 쓰기 작업을 처리한다.(MyISAM은 쓰기 버퍼링 기능을 사용할 수 없음)
이외에 인서트 버퍼를 병합하는 스레드, 데이터를 버퍼로 읽어오는 스레드, 데드락이나 잠금을 모니터링하는 스레드 등 다양한 작업들이 백그라운드 스레드에서 처리된다.
플러그인 스토리지 엔진 모델
MySQL의 독특한 구조 중 대표적인 것이 플러그인 모델이다.
수많은 사용자의 요구 조건을 만족시키기 위해 기본적으로 제공되는 스토리지 엔진 외에 부가적인 기능을 더 제공하는 스토리지 엔진을 개발하여 플러그인 형태로 제공할 수 있다. 따라서 플러그인 형태로 빌드된 스토리지 엔진 라이브러리를 다운로드해서 끼워 넣기만 하면 손쉽게 부가적인 기능도 사용할 수 있게 된다.
MySQL 서버에서는 스토리지 엔진뿐만 아니라 다양한 기능을 플러그인 형태로 지원한다.
인증이나 전문 검색 파서 또는 쿼리 재작성과 같은 플러그인이 있으며, 비밀번호 검증과 커넥션 제어 등에 관련된 다양한 플러그인이 제공된다. 이뿐만 아니라 MySQL 서버의 기능을 커스텀하게 확장하거나 새로운 기능을 플러그인을 이용해 구현할 수도 있다.
쿼리 실행 구조
쿼리 실행 관점에서 MySQL의 구조를 그림으로 표현하면 다음과 같다.
쿼리 파서
들어온 쿼리를 MySQL이 인식할 수 있는 어휘나 기호로 분리해 트리 형태의 구조로 만들어내는 작업을 수행한다. 쿼리의 기본 문법 오류가 이 과정에서 발견되고 사용자에게 오류 메시지를 전달하게 된다.
전처리기
파서 과정에서 만들어진 파서 쿼리를 기반으로 문장에 구조적 문제점을 확인한다.
테이블 이름이나 컬럼 이름, 내장 함수와 같은 개체를 매핑해 객체의 존재 여부와 접근 권한 등을 확인하는 과정을 수행한다.
옵티마이저
쿼리를 저렴한 비용으로 가장 빠르게 처리할지(최적화)를 결정하는 역할을 담당한다.
따라서 DBMS의 두뇌 역할을 한다고 볼 수 있다.
실행 엔진
계획대로 각 핸들러에게 요청해서 받은 결과를 또 다른 핸들러 요청의 입력으로 연결하는 역할을 수행한다. 옵티마이저가 두뇌라면 실행 엔진은 손과 발의 역할을 한다고 생각하면 쉽다.
핸들러(스토리지 엔진)
핸들러는 MySQL 서버의 가장 밑단에서 실행 엔진의 요청에 따라 데이터를 디스크로 저장하고 읽어오는 역할을 담당한다. 핸들러는 결국 스토리지 엔진을 의미한다.
쿼리 캐시
쿼리 캐시는 빠른 응답을 필요로 하는 웹 기반 응용 프로그램에서 매우 중요한 역할을 담당했지만, 테이블의 데이터가 변경되면 캐시에 저장된 결과에서 해당 테이블과 연관된 모든 것들을 삭제해야 하므로 동시 처리 성능 저하를 유발하는 문제점이 있었다.
결국 MySQL 8.0에서는 쿼리 캐시에 대한 기능이 완전히 제거되었고 관련된 시스템 변수도 모두 제거됐다.
스레드 풀
스레드 풀은 내부적으로 사용자의 요청을 처리하는 스레드 개수를 줄여서 동시 처리되는 요청이 많아도 CPU가 제한된 개수의 스레드 처리에만 집중할 수 있게 하여 서버 자원 소모를 줄이는 것이 목적이다.
CPU가 제한된 수의 스레드만으로 적절히 처리할 수 있도록 유도한다면 CPU의 프로세서 친화도를 높이고 불필요한 컨텍스트 스위치를 줄여 오버헤드를 낮출 수 있다는 장점이 있다. 따라서 일반적으로 스레드 그룹 개수는 CPU 코어의 개수와 맞추는 것이 좋다.
MySQL 서버 엔터프라이즈 에디션에서만 스레드 풀 기능을 지원하며 커뮤니티 에디션에서는 지원하지 않는다.
트랜잭션 지원 메타데이터
MySQL 서버 5.7 버전까지 테이블 구조를 파일 기반으로 관리했다.
하지만 이러한 파일 기반의 메타데이터는 생성 및 변경 작업에 트랜잭션을 지원하지 않아서 생성이나 변경 도중 MySQL 서버가 비정상 종료가 되면 일관성이 보장되지 않아 테이블이 깨지는 현상이 발생했다.
따라서 MySQL 8.0부터는 테이블 구조 정보나 스토어드 프로그램의 코드 관련 정보 등의 정보를 모두 InnoDB의 테이블에 저장되도록 변경되었다.