rastalion.dev
MYSQL

MySQL 아키텍쳐

teinam 2019-06-09updated 09-20 6 MIN

MySQL 서버는 크게 MySQL 엔진과 스토리지 엔진으로 구성됩니다. 아래 구성요소 이름과 설정값은 현행 LTS 인 MySQL 8.4 기준이고, 8.0 은 2026년 4월부터 Oracle Sustaining Support 단계라 새 시스템의 기준으로 삼기 어렵습니다.

MySQL 서버 아키텍처 An architecture diagram generated by Archify. 클라이언트 · C API / Connector · Architecture component 클라이언트 C API / Connector 커넥션 매니저 · 스레드 캐시 · Architecture component 커넥션 매니저 스레드 캐시 파서 · Architecture component 파서 옵티마이저 · Architecture component 옵티마이저 실행 엔진 · Architecture component 실행 엔진 데이터 딕셔너리 · mysql.ibd · Architecture component 데이터 딕셔너리 mysql.ibd 핸들러 API · Architecture component 핸들러 API InnoDB · 버퍼 풀 · Architecture component InnoDB 버퍼 풀 MyISAM · 키 캐시 · Architecture component MyISAM 키 캐시 파일 시스템 · Architecture component 파일 시스템 TCP/IP 쿼리 메타데이터 실행 계획 핸들러 호출 I/O I/O 범례 서버 계층 스토리지 파일 시스템 클라이언트
MySQL 서버의 계층 구조: 클라이언트부터 파일 시스템까지

MySQL 엔진은 클라이언트 접속과 쿼리 요청을 받는 커넥션 관리 계층, SQL 파서와 전처리기, 실행 계획을 고르는 옵티마이저가 중심을 이룹니다. 성능을 위한 캐시와 버퍼도 이 계층에 있습니다. 8.4 매뉴얼이 캐시·버퍼로 다루는 것은 InnoDB 버퍼 풀, MyISAM 키 캐시, 프리페어드 스테이트먼트와 스토어드 프로그램 캐시입니다. 오래된 자료가 구성요소로 함께 그리는 쿼리 캐시는 8.0 에서 제거돼 현행 버전에는 없습니다.

접근 인터페이스는 C API 와 Connector/J, Connector/ODBC, Connector/NET, Connector/Python, Connector/C++ 등의 커넥터로 제공되므로 C/C++, Java, PHP, Python 같은 언어에서 같은 서버에 붙을 수 있습니다. ODBC 는 0 부터 3.51 레벨까지 지원합니다. 다만 매뉴얼은 “속도와 신뢰성을 해치지 않는 범위에서 SQL 표준 준수를 향해 계속 나아간다”는 목표를 밝히면서 표준에 대한 확장이 많다는 점도 함께 적어 둡니다. 특정 표준 판본에 완전히 부합한다고 선언하지는 않으므로, 표준 문법으로 작성한 쿼리가 다른 DBMS 와 그대로 호환된다고 전제할 수는 없습니다.

MySQL 은 스토리지 엔진을 플러그인 방식으로 끼우는 구조라서 필요에 따라 엔진을 골라 데이터베이스를 구성할 수 있습니다. InnoDB 는 5.5 부터 기본 엔진이고 8.4 에서도 기본값입니다. MySQL 엔진이 SQL 을 분석하고 최적화하는 두뇌라면, 스토리지 엔진은 실제 데이터를 디스크에 쓰고 디스크에서 읽어 오는 일을 맡습니다. MySQL 엔진은 하나지만 스토리지 엔진은 여러 개를 동시에 쓸 수 있고, 테이블마다 사용할 엔진을 지정해 그 테이블의 읽기·쓰기 동작을 해당 엔진이 처리하게 합니다. 두 계층 사이의 요청은 핸들러를 통해 오가며, 핸들러 호출 횟수는 Handler_read_key 같은 상태 변수로 확인할 수 있습니다.

데이터 딕셔너리

8.0 부터 서버는 트랜잭셔널 데이터 딕셔너리로 객체 메타데이터를 관리합니다. 이전에 메타데이터를 담았던 파일은 모두 없어지고 그 내용이 데이터 딕셔너리 테이블로 들어갔습니다. 사라진 파일은 .frm(테이블 정의), .par(파티션 정의), .TRN·.TRG(트리거), .isl(데이터 디렉터리 밖에 만든 테이블스페이스의 위치), db.opt(데이터베이스 기본 문자셋), ddl_log.log 입니다.

mysql 시스템 테이블과 데이터 딕셔너리 테이블은 데이터 디렉터리의 mysql.ibd 라는 하나의 InnoDB 테이블스페이스에 저장되고, 5.7 까지 MyISAM 이던 권한 테이블도 InnoDB 테이블이 됐습니다. .frm 이 없어지면서 그 구조가 강제하던 테이블 정의 64KB 제한도 함께 사라졌습니다. 다만 information_schema.TABLESVERSION 칼럼은 5.7 의 마지막 .frm 버전인 10 을 고정값으로 보고합니다.

MySQL 스레드

MySQL 은 프로세스 기반이 아니라 스레드 기반으로 동작하며, 스레드는 크게 Foreground Thread 와 Background Thread 로 나뉩니다.

Foreground Thread (클라이언트 스레드)

커넥션 매니저 스레드가 네트워크 인터페이스에서 접속 요청을 받아 커넥션마다 전용 스레드를 붙입니다. 이 스레드가 해당 커넥션의 인증과 요청 처리를 담당하므로, 접속한 클라이언트 수만큼 스레드가 존재합니다.

커넥션이 끝나면 그 스레드는 캐시가 꽉 차 있지 않은 한 스레드 캐시로 돌아갑니다. 캐시 크기는 thread_cache_size 가 정하고 기본값은 서버가 기동할 때 자동으로 산정합니다. 값이 0 이면 캐시를 쓰지 않고 접속마다 스레드를 새로 만들고 종료할 때 버립니다. 캐시에 남은 스레드 수와 캐시에서 꺼내지 못해 새로 만든 스레드 수는 Threads_cached·Threads_created 상태 변수로 확인합니다.

NOTE — 스레드 캐시와 스레드 풀은 다른 기능입니다. 커넥션당 스레드 하나가 기본 모델이고, 여러 커넥션을 제한된 실행 스레드 집합에 다중화하는 MySQL Enterprise Thread Pool 은 MySQL Enterprise Edition 에 포함된 상용 플러그인이라 커뮤니티 빌드에는 없습니다.

데이터는 버퍼나 캐시에서 먼저 찾고, 없으면 디스크의 데이터·인덱스에서 직접 읽어 처리합니다. MyISAM 테이블은 디스크 쓰기까지 Foreground Thread 가 처리하지만, InnoDB 테이블은 버퍼까지만 이 스레드가 다루고 버퍼에서 디스크로 내리는 일은 Background Thread 가 맡습니다.

Background Thread

InnoDB 에서는 많은 작업이 Background Thread 로 넘어갑니다. 로그를 디스크에 기록하는 스레드, 버퍼 풀의 변경된 페이지를 디스크에 내리는 스레드, 데이터를 버퍼 풀로 읽어 들이는 스레드, 락과 데드락을 감시하는 스레드, 이들을 총괄하는 메인 스레드가 역할별로 나뉘어 동작합니다.

세컨더리 인덱스 변경을 모아 두는 체인지 버퍼도 이 계층에 있습니다. 8.4 는 innodb_change_buffering 기본값이 none 이라 기본 설정에서는 버퍼링을 하지 않고, 버퍼링을 켜서 쌓인 변경은 서버가 거의 유휴일 때와 slow shutdown 중에 InnoDB 메인 스레드가 병합합니다.

I/O 스레드 개수는 다음과 같습니다.

파라미터 기본값 범위
innodb_read_io_threads 논리 프로세서 수의 절반, 최소 4 1~64
innodb_write_io_threads 4 1~64

두 값은 동적으로 바꿀 수 없어 옵션 파일(my.cnf)에 적고 서버를 재시작해야 합니다. 매뉴얼이 제시하는 조정 기준은 대기 중인 읽기 요청 수입니다. SHOW ENGINE INNODB STATUS 출력에서 대기 중인 읽기 요청이 64 × innodb_read_io_threads 를 넘으면 읽기 스레드를 늘려 볼 만합니다. 백그라운드 스레드 하나가 최대 256개의 대기 I/O 요청을 처리하며, 리눅스에서는 데이터 파일 페이지의 읽기 선행·쓰기 요청에 기본적으로 비동기 I/O 서브시스템을 쓰기 때문에 이 스레드들이 요청을 처리하는 방식이 달라집니다.

메모리 할당과 사용 구조

MySQL 이 사용하는 메모리 공간은 글로벌 메모리 영역과 로컬 메모리 영역으로 나뉩니다.

글로벌 메모리 영역

서버가 기동할 때 옵션 파일에 설정한 값만큼 OS 로부터 확보하는 영역입니다. 보통 클라이언트 스레드 수와 무관하게 하나의 공간만 할당하지만, 필요에 따라 두 개 이상을 할당받기도 합니다. 개수와 상관없이 모든 스레드가 공유합니다. InnoDB 버퍼 풀, InnoDB 로그 버퍼, MyISAM 키 캐시, 테이블 캐시가 여기에 속합니다.

로컬 메모리 영역

세션 메모리 영역이라고도 하며, 클라이언트 스레드가 쿼리를 처리하는 데 쓰는 공간입니다. 커넥션 버퍼(net_buffer_length), 정렬 버퍼(sort_buffer_size), 조인 버퍼(join_buffer_size), 읽기 버퍼(read_buffer_size), 랜덤 읽기 버퍼(read_rnd_buffer_size) 등이 있습니다. 클라이언트 스레드별로 독립적으로 할당되고 공유되지 않습니다. 커넥션 버퍼처럼 세션이 살아 있는 동안 계속 유지되는 공간이 있고, 정렬 버퍼나 조인 버퍼처럼 쿼리를 실행할 때 할당하고 끝나면 반환하는 공간이 있습니다.

플러그인 스토리지 엔진

MySQL 의 가장 독특한 구조가 스토리지 엔진의 플러그인 모델입니다. 스토리지 엔진뿐 아니라 전문 검색용 파서도 플러그인으로 끼울 수 있습니다. MySQL 엔진이 스토리지 엔진을 조정할 때 쓰는 것이 위에서 설명한 핸들러입니다. 어떤 엔진을 쓰느냐에 따라 읽기·쓰기 처리 방식이 달라지고, 그 차이가 성능 차이로도 나타납니다.

현재 서버에 어떤 엔진이 들어 있는지는 SHOW ENGINES 로 확인합니다.

SHOW ENGINES;

8.4 매뉴얼이 예로 든 출력에는 아래 여덟 개 엔진이 나옵니다.

엔진 Support 트랜잭션
InnoDB DEFAULT YES
MyISAM YES NO
MEMORY YES NO
MRG_MYISAM YES NO
CSV YES NO
ARCHIVE YES NO
BLACKHOLE YES NO
PERFORMANCE_SCHEMA YES NO

Support 가 YES 면 그 빌드에 포함돼 사용할 수 있는 엔진이고, DEFAULTENGINE 옵션 없이 테이블을 만들 때 쓰이는 기본 엔진입니다. NODISABLED 도 나올 수 있습니다. 매뉴얼도 적어 두었듯 실제 목록은 MySQL 버전과 빌드 구성에 따라 달라집니다. 원하는 엔진이 목록에 없으면 플러그인으로 빌드된 라이브러리를 설치해 추가할 수 있습니다.

MyISAM 은 8.4 에도 남아 있지만 기본 엔진이 아니므로 ENGINE = MyISAM 을 명시해야 사용합니다. 파티셔닝은 지원하지 않아서, 이전 버전에서 만든 파티션된 MyISAM 테이블은 8.4 에서 그대로 쓸 수 없고 InnoDB 로 바꾸거나 파티션을 제거해야 합니다.

Advertisement