MySQL 구조
MySQL 은 크게 요청을 받아와서 이해하는 머리 역할의 MySQL 엔진과 그 처리를 담당하는 스토리지 엔진으로 구분할 수 있습니다.
MySQL 엔진은 주로 각 프로그래밍 언어에 대한 연결과 SQL 분석 및 최적화를 담당합니다.
반면 스토리지 엔진은, MySQL 엔진에서 분석한 명령들을 실제로 디스크에 I/O 하는 기능을 주로 담당한다고 보면 됩니다.
mysql> CREATE TABLE test(c1 INT, c2, INT) ENGINE=INNODB;
ENGINE='엔진' 명령어는 해당 테이블에 대한 I/O 작업을 할 스토리지 엔진을 지정할 수 있습니다.
위 명령을 수행하면, InnoDB 스토리지 엔진이 test 테이블에 대한 CRUD 처리를 담당하게 됩니다.
핸들러 API
MySQL 엔진의 쿼리실행기에서 쓰기나 읽기 작업을 실행시키면, 스토리지 엔진에 쓰기 읽기 작업을 요청하게 됩니다.
이를 '핸들러 요청' 이라고 하며, 해당 요청들을 처리하는 API 를 '핸들러 API' 라고 합니다.
MySQL 엔진과 스토리지 엔진이 소통하는 핸들러 API 를 만족하게 구현하면 누구든지 스토리지 엔진을 구현하여 추가할 수 있습니다.
두 엔진 사이의 확장성을 위한 인터페이스 혹은 프록시 라고 생각하시면 이해가 쉽습니다.
SHOW GLOBAL STATUS LIKE 'Handler%';
위 명령으로 Handler API 로 처리된 레코드 수를 확인할 수 있습니다.
MySQL 스레딩 구조
MySQL 서버는 프로세스 구조가 아닌 스레드 구조로 동작합니다.
performance_Schema 의 threads 테이블에서 현재 실행중인 스레드를 확인할 수 있습니다.
mysql> SELECT * FROM performance_schema.threads;
MySQL 스레드는 크게 클라이언트의 요청처리와 캐싱등을 담당하는 포그라운드 스레드와
실제 I/O 를 처리하는 백그라운드 스레드로 구분합니다.
포그라운드 스레드
포그라운드 스레드는 각 클라이언트의 요청처리를 담당하는 만큼, 최소한 접속한 클라이언트 수만큼은 존재합니다.
접속한 클라이언트의 커넥션이 종료될 시, 해당 스레드는 종료되지 않고 thread_cache_size 시스템 변수의 수만큼은
스레드 캐시로 저장됩니다. (스레드과 유사한 개념이라고 보면 될 것 같습니다.)
MyISAM 스토리지는 디스크까지 포그라운드 스레드가 처리하지만
InnoDB 의 경우 포그라운드 스레드는 버퍼나 캐시까지만 처리합니다.
백그라운드 스레드
InnoDB 의 경우 백그라운드 스레드가 많은 영역을 담당하는데, 그 주요 영역들을 살펴봅시다.
- Insert Buffer 병행 스레드
- Log I/O 쓰레드
- InnoDB 버퍼풀에 존재하는 데이터를 읽어서 디스크에 쓰는 쓰레드
- 디스크의 데이터를 버퍼에 읽어오는 쓰레드
- 잠금이나 데드락을 감지하는 쓰레드
등이 존재합니다.
일반적으로 DBMS 는 쓰기작업은 버퍼에 (지연쓰기)하여 일괄처리 합니다.
이러한 작업의 처리를 백그라운드 스레드가 담당합니다.
MysQL 5.5 버전부터는 읽기 쓰기 스레드의 수를 여러개로 지정할 수 있습니다.
innodb_write_io_threads, inoodb_read_io_threads 시스템 변수로 조절이 가능합니다.
환경에 따라 적절한 수를 지정하면 성능상 큰 이점을 얻을 수 있습니다.
MySQL 메모리 공간
MySQL의 메모리 공간은 글로벌 영역과 로컬 영역으로 나눌 수 있습니다.
글로벌 영역은 MySQL 서버가 실행될 때 운영체제로 부터 메모리 공간을 할당받아, 스레드간 공유되는 메모리영역을 뜻하며,
로컬 영역(= 세션 영역)은 클라이언트 스레드별 격리되는 메모리 공간을 뜻합니다.
글로벌 영역
일반적으로 단 하나만 사용하지만, 설정에 따라 N 개가 될 수 도있습니다.
N개의 글로벌 영역이 있더라도, 모든 스레드에 의해 공유됩니다.
- 테이블 캐시
- InnoDB Buffer Pool
- InnoDB 어댑티브 해시 인덱스
- InnoDB 리두 로그 버퍼
등이 대표적입니다. 각각의 영역에 대한 역할이나 기능은 후술하겠습니다.
로컬 영역
MySQL 서버에 접속한 클라이언트의 쿼리를 처리하는 영역입니다.
- 조인 버퍼
- Sort 버퍼
- 네트워크 버퍼
- 바이너리 로그 캐시
등이 대표적입니다.
로컬 영역은 사용 시에만 할당되는 영역(Sort 버퍼, 조인 버퍼)과
클라이언트의 커넥션이 연결되는 시점부터 종료될 때까지 쭉 존재하는 영역(커넥션 버퍼, 결과 버퍼)이 있습니다.
플러그인 스토리지
플러그인은 MySQL의 독특한 특징입니다.
위에서 설명한 스토리지 엔진은 Handler API 만 만족하면, 누구나 구현이 가능하다고 설명했습니다.
플러그인 이러한 스토리지 엔진과 스토리지엔진 이외에도 인덱스 키워드 분리를 담당하는 검색어 파서나
사용자 인증 알고리즘(Native Authentication, Caching SHA-2) 등도 플러그인으로 제공됩니다.
SHOW ENGINES; # 스토리지 엔진 조회
SHOW PLUGINS; # 스토리지 엔진 이외의 플러그인 조회
플러그인은 플러그인 끼리 통신할 수 없고, MySQL 서버의 함수나 변수를 직접 호출한다는 단점이 존재하는데,
이를 해결하기 위해 8.0 부터는 컴포넌트를 지원합니다. mysql.components 테이블로 컴포넌트를 확인할 수 있습니다.
쿼리 실행 구조
MySQL 쿼리 실행구조를 도식화 한 그림입니다.
사용자가 쿼리를 실행시키면
1. 쿼리파서가 쿼리를 MySQL이 인식할 수 있는 최소 단위인 토큰으로 분리해 트리구조로 만듭니다.
쿼리의 기본 문법오류는 여기서 걸러집니다.
2. 전처리기에서 파서가 만든 트리를 기반으로 전처리기에서 존재하지 않는 테이블이나 칼럼등의 객체에 요청하지는 않았는지,
접근 권한이 없는 객체에 접근하지는 않았는지 등의 쿼리의 구조적인 문제가 없는지 확인합니다.
3. 쿼리에서 발생할 수 있는 오류가 모두 걸러졌다면 옵티마이저가 쿼리를 가장 저렴한 비용으로 빠르게 처리할 수 있도록 처리방법을 결정합니다. 옵티마이저가 각 상황에서 더 나은 결정을 내릴 수 있도록 설정하는 방법을 공부하면서 알아갈 것 입니다.
4. 마지막으로 실행엔진이 옵티마이저가 지시한 방법을 바탕으로 핸들러 API 를 통하여 스토리지 엔진과 통신하며 쿼리를 수행하고, 사용자에게 결과를 리턴합니다.
메타데이터의 트랜잭션 지원
5.7 버전까지는 테이블 구조를 FRM 형식의 파일에 저장하고, 일부 스토어드 프로그램 역시 파일 기반으로 관리하였습니다.
하지만 파일 기반의 메타데이터 생성이나 변경 작업은 트랜잭션을 지원하지 않아, 실행 중 비정상적으로 서버가 종료되었을 경우,
'테이블이 깨졌다' 라고 말하는 현상이 발생하였었습니다.
8.0 부터는 메타데이터 정보를 InnoDB 스토리지엔진의 mysql DB에 저장하도록 하였습니다.
mysql DB는 mysql.ibd 라는 테이블스페이스에 저장됩니다.
하지만 사용자가 임의로 수정하지 못하도록 사용자는 mysql DB의 테이블 구조가 저장된 테이블은 보이지 않도록 막아두고 information_Schema 의 TABLES 나 COLUMNS 같은 뷰로 제공하고 있습니다.
위와 같은 변경으로 인해 InnoDB 스토리지 엔진의 경우
스키마 변경작업이 원자성있도록 모두 생성되던지, 생성되지 않던지 안전한 상태로 생성되게 됩니다.
이번 포스팅에서는 MySQL 의 전체적인 구조와 MySQL 엔진을 살펴보았고
실무에서 일반적으로 InnoDB 스토리지 엔진을 많이 사용하므로 MyISAM 보다는 InnoDB 위주로 요약해보았습니다.
다음 포스팅에서는 InnoDB 스토리지 엔진의 구조와 MyISAM 과 InnoDB의 차이에 대해 살펴보겠습니다.
'Database' 카테고리의 다른 글
[REAL MYSQL 8.0] InnoDB 스토리지 아키텍쳐 (0) | 2022.04.04 |
---|---|
RDS Slow Query Log 세팅 및 쿼리 개선 (0) | 2022.03.22 |
Redis 자료구조와 활용 예시 (0) | 2022.03.16 |
[Real MySQL 8.0] 계정과 역할 (0) | 2022.03.08 |
[Real MySQL 8.0] MySQL 서버 설정과 시스템 변수 (0) | 2022.03.05 |