MySQL 서버는 사람의 머리 역할을 담당하는 MySQL엔진과 손발 역할을 담당하는 스토리지 엔진으로 구분할 수 있다.
MySQL 엔진
클라이언트로부터의 접속 및 쿼리 요청을 처리하는 커넥션 핸들러와 SQL 파서 및 전처리기, 쿼리의 최적화된 실행을 위한 옵티마이저로 구성되어 있다.
1. 핸들러 API
쿼리 실행기에서 데이터를 쓰거나 읽어야 할 때는 각 스토리지 엔진에 쓰기 또는 읽기를 요청하는데, 이러한 요청을 처리하는 API를 핸들러 API라고 한다.
2. MySQL 스레딩 구조
MySQL 서버는 스레드 기반으로 작동하며, 포그라운드 스레드와 백그라운드 스레드로 구분할 수 있다.
- 포그라운드 스레드
-> 클라이언트가 MySQL 서버에 접속하면 서버는 클라이언트의 요청을 처리해 줄 스레드를 생성해 할당한다. 이 스레드는 DBMS 앞단에서 사용자와 통신하기 때문에 포그라운드 스레드라고 하며, 사용자가 요청한 작업을 처리하기 때문에 사용자 스레드라고도 한다.
-> 포그라운드 스레드는 데이터를 MySQL의 데이터 버퍼나 캐시로부터 가져오며, 버퍼나 캐시에 없는 경우에는 직접 디스크의 데이터나 인덱스 파일로부터 데이터를 읽어와서 작업을 처리한다. - 백그라운드 스레드
InnoDB는 여러 가지 작업이 백그라운드로 처리된다.
1 ) 인서트 버퍼를 병합하는 스레드
2 ) 로그를 디스크로 기록하는 스레드
3 ) InnoDB 버퍼 풀의 데이터를 디스크에 기록하는 스레드
4 ) 데이터를 버퍼로 읽어 오는 스레드
5 ) 잠금이나 데드락을 모니터링하는 스레드 : 쓰기 스레드는 아주 많은 작업을 백그라운드로 처리하기 때문에 충분한 수의 스레드를 할당하는 것이 좋으며, 쓰기 작업은. 지연(버퍼링)되어 처리될 수 있다.
3. 메모리 할당 및 사용 구조
MySQL에서 사용되는 메모리 공간은 스레드가 공유해서 사용하는 공간인지 여부에 따라 글로벌 메모리 영역과 로컬 메모리 영역으로 구분된다.
[글로벌 메모리 영역]
클라이언트 스레드의 수와 무관하게 하나의 메모리 공간만 할당된다.
1 ) 테이블 개시
2 ) InnoDB 버퍼 풀
3 ) InnoDB 어댑티브 해시 인덱스
4 ) InnoDB 리두 로그 버퍼
[로컬 메모리 영역]
클라이언트 스레드가 쿼리를 처리하는 데 사용하는 메모리 영역이다. 클라이언트와 MySQL 서버와의 커넥션을 세션이라고 하기 때문에 세션 메모리 영역이라고도 표현한다. 로컬 메모리는 각 클라이언트 스레드별로 독립적으로 할당되며 절대 공유되어 사용되지 않는다.
1 ) 커넥션 버퍼
2 ) 정렬 버퍼
4. 플러그인 스토리지 엔진 모델
MySQL는 다른 DBMS과 달리 플러그인 모델 구조를 가지고 있다. 기본적으로 많은 스토리지 엔진을 가지고 있지만, 사용자의 요구 조건을 만족시키기 위해 사용자가 직접 스토리지 엔진을 개발하는 것도 가능하다. MySQL에서 쿼리가 실행되는 과정을 보면 대부분의 작업이 MySQL 엔진에서 처리되고, '데이터 읽기/쓰기' 작업에만 스토리지 엔진이 사용된다.
MySQL 엔진은 각 스토리지 엔진에게 데이터를 읽어오거나 저장하도록 명령하기 위해 핸들러를 이용한다.
MySQL에서는 스토리지 엔진뿐만 아니라 다양한 기능을 플러그인 형태로 지원한다. 인증이나 전문 검색 파서 또는 쿼리 재작성과 같은 플러그인이 있으며, 비밀번호 검증과 커넥션 제어 등에 관련된 다양한 플러그인이 제공된다.
5. 컴포넌트
MySQL 서버 플러그인은 다음과 같은 단점이 있다.
1 ) 플러그인은 오직 MySQL 서버와 인터페이스할 수 있고, 플러그인끼리는 통신할 수 없다.
2 ) 플러그인은 MySQL 서버의 변수나 함수를 직접 호출하기 때문에 안전하지 않다.
3 ) 플러그인은 상호 의존 관계를 설정할 수 없어서 초기화가 어렵다
이러한 문제를 해결하기 위해 컴포넌트가 등장했다.
6. 쿼리 실행 구조
- 쿼리 파서 : 사용자 요청으로 들어온 쿼리 문장을 MySQL이 인식할 수 있는 토큰으로 분리해 트리 형태의 구조로 만들어 내는 작업을 한다. 쿼리 문장의 기본 문법 오류는 이 과정에서 발견된다.
- 전처리기 : 파서 과정에서 만들어진 파서 트리를 기반으로 쿼리 문장에 구조적인 문제점이 있는지 확인한다. 각 토큰을 테이블 이름이나 칼럼 이름, 내장 함수와 같은 개체를 매핑해 해당 객체의 존재 여부와 객체의 접근 권한 등을 확인한다.
- 옵티마이저 : 사용자의 요청으로 들어온 쿼리 문장을 어떻게 저렴한 비용으로 가장 빠르게 처리할지를 결정하는 역할을 담당한다.
- 실행 엔진 : 옵티마이저가 두뇌라면 실행 엔진과 핸들러는 손과 발에 비유할 수 있다. 옵티마이저가 실행 계획을 세우면 그 계획대로 각 핸들러에게 요청해서 받은 결과를 또 다른 핸들러 요청의 입력으로 연결하는 역할을 한다.
- 핸들러(스토리지 엔진) : MySQL 실행 엔진의 요청에 따라 데이터를 디스크로 저장하고 읽어 오는 역할을 담당한다.
- 쿼리 캐시(8.0에서 제거됨) : SQL의 실행 결과를 메모리에 캐시하고, 동일 sql 쿼리가 실행되면 테이블을 읽지 않고 즉시 결과를 반환하기 때문에 빠른 성능을 보여줬다. 쿼리 캐시는 테이블의 데이터가 변경되면 캐시에 저장된 결과 중에서 변경된 테이블과 관련된 것들은 모두 삭제해야 했고 이는 심각한 동시 처리 성능 저하와 버그를 유발해 8.0 버전 이후에는 삭제되었다.
- 스레드 풀 : MySQL 서버의 CPU가 제한된 개수의 스레드 처리에만 집중할 수 있게 해서 서버의 자원 소모를 줄이는 것이 목적이다. 스케줄링 과정에서 CPU 시간을 제대로 확보하지 못하는 경우에는 쿼리 처리가 더 느려지기 때문에 스케줄링 과정에서 CPU시간을 확보하는 것이 중요하다.
스레드 풀의 모든 스레드가 일을 처리하고 있다면 어떤식으로 처리 될까? => 스레드 풀의 타이머 스레드는 주기적으로 스레드 그룹의 상태를 체크해서 시스템 변수에 정의된 시간만큼 작업을 끝내지 못하면 새로운 스레드를 생성해서 스레드 그룹에 추가한다.