Architecture
1 Architecture

- MySQL 서버는 다른 DMBS에 비해 아키텍처가 독특하다
- MySQL 서버는 크게 MySQL 엔진과 스토리지 엔진으로 구분된다
- MySQL 서버에서 MySQL 엔진은 하나지만 스토리지 엔진은 여러개를 동시에 사용할 수 있다
2 MySQL 엔진 아키텍처
- 클라이언트로부터 접속 및 쿼리 요청을 처리하는 커넥션 핸들러와 SQL 파서 및 전처리기 그리고 쿼리의 최적화된 실행을 위한 옵티마이저가 중심을 이룬다
- 실질적인 GROUP BY나 ORDER BY 등 많은 복잡한 처리는 스토리지 엔진 영역이 아니라 MySQL 엔진의 처리 영역인 쿼리 실행기에서 처리된다
2.1 쿼리 파서
- 사용자 요청으로 들어온 쿼리 문장을 토큰(MySQL이 인식할 수 있는 최소 단위의 어휘나 기호)으로 분리해 트리 형태의 구조로 만들어 내는 작업을 의미한다.
- 쿼리문장의 기본 문법 오류는 이 과정에서 발견되며 사용자에게 오류 메시지를 전달하게 된다.
2.2 전처리기
- 파서 과정에서 만들어진 파서 트리를 기반으로 쿼리 문장에 구조적인 문제점이 있는지 확인한다.
- 각 토큰을 테이블 이름이나 칼럼 이름 또는 내장 함수와 같은 개체를 매핑에 해당 객체의 존재 여부와 객체의 접근권한 등을 확인하는 과정을 이 단계에서 수행한다.
- 존재 하지 않거나 권한상 사용할 수 없는 개체의 토큰은 이 단계에서 걸러진다.
2.3 옵티마이저
- 사용자의 요청으로 들어온 쿼리 문장을 저렴한 비용으로 가장 빠르게 처리할지 결정하는 역할을 하는 DBMS의 두뇌다
- 옵티마이저가 더 나은 선택을 할 수 있게 유도하는 것이 아주 중요하다
2.4 실행엔진
- 실행 엔진은 만들어진 계획대로 각 핸들러에게 요청해서 받은 결과를 또 다른 핸들러 요청의 입력으로 연결하는 역할을 수행한다.
예시
- 옵티마이저가 GROUP BY를 처리하기 위해 임시 테이블을 사용하기로 결정했다고 가정했을때,
- 실행 엔진은 핸들러에게 임시 테이블을 만들라고 요청
- 다시 실행 엔진은 WHERE 절에 일치하는 레코드를 읽어오라고 핸들러에 요청
- 읽어온 레크코들을 1번에서 준비한 임시 테이블에 저장하라고 다시 핸들러에 요청
- 데이터가 준비된 임시 테이블에서 필요한 방식으로 데이터를 읽어 오라고 핸들러에게 다시 요청
- 최종적으로 실행 엔진은 결과를 사용자나 다른 모듈로 넘긴다
- ex) 실행엔진이 핸들러에게
테이블을 만들어,WHERE 절에 일치하는 레코드 읽어와,읽어온 레코드들 임시 테이블로 저장해등을 요청하고 최종적으로 실행 엔진은 결과를 사용자나 다른 모듈에게 넘긴다.
3 스토리지 엔진
- MySQL 엔진은 요청된 SQL 문장을 분석하거나 최적화하는등 DBMS의 두뇌에 해당하는 처리를 수행하고 실제 데이터를 디스크 스토리지에 저장하거나 데이터를 읽어오는 부분은 스토리지 엔진이 담당한다
- MySQL 엔진은 하나지만 여러 스토리지 엔진을 동시에 사용할 수 있다
- 아래와 같이 테이블이 사용할 스토리지 엔진을 지정하면 이후 테이블의 모든 읽기 작업이나 변경 작업은 정의된 스토 리지 엔진이 처리한다.
mysql > CREATE TABLE test_table (fd1 IT, fd2 INT) ENGINE = INNODB;
3.1 핸들러 API
- MySQL 엔진의 쿼리 실행기에서 데이터를 쓰거나 읽어야 할 때는 각 스토리지 엔진에게 쓰기 또는 읽기를 요청하는데,
- 이러한 요청을 핸들러(Handler) 요청이라고 하고, 여기서 사용되는 API를 핸들러 API라고 한다.
"SHOW GLOBAl STATUS LIKE 'Handler%';"
- 위 명령으로 이 핸들러 API를 통해 얼마나 많은 데이터(레코드) 작업이 있엇는지 확인가능하다.
3.2 핸들러
- 핸들러는 MySQL 서버의 가장 밑단에서 실행 엔진의 요청에 따라 데이터를 디스크로 저장하고 읽어오는 역할을 담당한다
- 핸들러가 결국 스토리지 엔진을 의미한다
- MyISAM 테이블을 조작하는 경우 핸들러가 MyISAM 스토리지 엔진이 되고 InnoDB 테이블을 조작하는 경우 핸들러가 InnoDB 스토리지 엔진이 된다
4 MySQL 스레딩 구조
- MySQL은 프로세스 기반이 아니라 스레드 기반으로 작동하며, 크게 포그라운드 스레드와 백그라운드 스레드로 구분할 수 있다.
4.1 포그라운드 스레드(클라이언트 스레드)
- 포그라운드 스레드는 최소한 MySQL 서버에 접속된 클라이언트의 수만큼 존재한다
- 주로 각 클라이언트 사용자가 요청하는 쿼리 문장을 처리하는 것이 임무다.
- 사용자가 작업을 마치고 커넥션을 종료하면, 해당 커넥션을 담당하던 스레드는 다시 스레드 캐시(Thread Cache)로 되돌아간다.
- 이때 이미 스레드 캐시에 일정 개수 이상의 대기 중인 스레드가 있으면 스레드 캐시에 넣지 않고 스레드를 종료시켜 일정 개수의 스레드만 스레드 캐시에 존재하게 한다.
- 이렇게 스레드의 개수를 일정하게 유지하게 만들어주는 파라미터가
thread_cache_size다. - InnoDB 테이블은 데이터 버퍼나 캐시까지만 포그라운드 스레드가 처리하고, 나머지 버퍼로부터 디스크까지 기록하는 작업은 백그라운드 스레드가 처리한다.
4.2 백그라운드 스레드
- 인서트 버퍼를 병합하는 스레드 로그를 디스크로 기록하는 스레드, InnoDB 버퍼 풀의 데이터를 디스크에 기록하는 스레드, 데이터를 버퍼로 읽어들이는 스레드, 그리고 기타 여러 가지 잠금이나 데드락을 모니터링 하는 스레드가 있다.
- 이러한 모든 스레드를 총괄하는 메인 스레드도 있다.
- 가장 중요한 것은 로그 스레드(Log thread)와 버퍼의 데이터를 디스크로 내려쓰는 작업을처리하는 쓰기 쓰레드 일 것이다.
- 쓰기 스레드의 개수 지정:
innodb_write_io_threads - 읽기 스레드의 개수 지정:
innodb_read_io_threads
5 메모리 할당 및 사용 구조
- MySQL에서 사용하는 메모리 공간은 크게 글로벌 메모리 영역과 로컬 메모리 영역으로 구분된다
- 글로벌 메모리 공간은 많은 스레드가 공유 해서 사용하고 로컬 메모리 영역은 그렇지 않다
5.1 글로벌 메모리 영역
글로벌 메모리 영역의 메모리 할당
- 클라이언트의 스레드 수의 무관하게 하나의 메모리 공간만 할당된다
- 필요에 따라 2개 이상의 공간을 할당 받을 수 있지만 이는 클라이언트의 스레드 수와 무관하다
- 글로벌 메모리 영역의 모든 메모리 공간은 MySQL 서버가 시작되면서 운영체제로부터 할당된다
- 요청된 메모리 공간을 100% 할당하지 않을 수 있다
- 각 운영체제의 메모리 할당 방식이 상당히 복잡하여 MySQL 서버가 사용하는 정확한 메모리의 양을 측정하는 것이 어렵다
대표적인 글로벌 메모리 영역
- 테이블 캐시
- InnoDB 버퍼 풀
- InnoDB 어댑티브 해시 인덱스
- InnoDB 리두 로그 버퍼
5.2 로컬 메모리 영역
- 클라이언트가 MySQL 서버에 접속하면 클라이언트 커넥션으로부터의 요청을 처리하기 위해 클라이언트 스레드를 하나씩 할당한다
- MySQL 서버상에 존재하는 클라이언트 스레드가 쿼리를 처리하는 데 사용하는 메모리 영역이다
- 로컬 메모리는 각 클라이언트 스레드별로 독립적으로 할당되어 절대 공유하지 않는다
로컬 메모리 영역의 다른 이름들
- 클라이언트 스레드가 사용하는 공간이라는 의미로 클라이언트 메모리 영역이라 부르기도 한다
- 클라이언트와 MySQL 서버와의 커넥션을 세션이라고 하기 때문에 로컬 메모리 영역을 세션 메모리 영역이라 부르기도 한다
로컬 메모리 사용 구조
- 로컬 메모리 공간의 특징으로 각 쿼리의 용도별로 필요할 때만 공간이 할당되고 그렇지 않은 경우 할당조차 하지 않을 수 있다
- 커넥션이 열려 있는 동안 계속 할당된 상태로 남아 있는 공간
- 커넥션 버퍼, 결과 버퍼
- 쿼리를 실행하는 순간에만 할당하고 다시 해제하는 공간
- 소트 버퍼, 조인 버퍼
대표적인 로컬 메모리 영역
- 커넥션 버퍼
- 정렬 버퍼
- 조인 버퍼
- 바이너리 로그 캐시
- 네트워크 버퍼
5 InnoDB 스토리지 엔진 아키텍처
- InnoDB는 MySQL에서 사용할 수 있는 스토리지 엔진 중 거의 유일하게 레코드 기반 잠금을 제공한다
- 따라서 높은 동시성 처리가 가능하고 안정적이며 성능이 뛰어나다