MySQL은 어떤 구조를 가질까?
MySQL은 어떠한 구조를 갖고 있을까요?
MySQL 전체 구조
MySQL은 크게 MySQL엔진과 스토리지 엔진이 있습니다. 이를 합쳐서 MySQL 서버라고 부릅니다.
MySQL엔진은 무엇일까요?
MySQL 엔진
MySQL 엔진은 MySQL 서버의 핵심 구성 요소로, 사용자가 실행한 SQL 쿼리를 해석하고 최적화하며, 적절한 스토리지 엔진을 통해 데이터를 저장하거나 조회하는 역할을 합니다. 이를 위해 MySQL 엔진은 여러 개의 내부 모듈로 구성되어 있습니다.
그림에서와 같이 커넥션 핸들러, SQL 인터페이스, SQL 파서, SQL 옵티마이저, 캐시 & 버퍼가 있습니다.
쉽게 말해, MySQL엔진은 사람의 머리 역할을 담당한다고 생각하면 됩니다.
각 모듈은 어떠한 역할을 할까요?
이제 MySQL 엔진의 각 구성 요소를 하나씩 살펴보겠습니다.
커넥션 핸들러
MySQL 서버에 클라이언트가 접속하면, 커넥션 핸들러가 이를 관리합니다. 이름 그대로 커넥션을 관리해줍니다.
- 클라이언트 요청을 받아들이고, 인증 및 권한을 확인합니다.
- 사용자의 요청을 처리할 스레드를 생성합니다.
- 일정 시간 동안 사용되지 않는 연결을 자동으로 종료하여 리소스를 효율적으로 관리합니다.
SQL 인터페이스
사용자가 입력한 SQL 문을 MySQL 서버에서 처리할 수 있도록 변환하는 역할을 합니다.
- SELECT, INSERT, UPDATE, DELETE 등의 SQL 문을 해석하여 내부적으로 처리할 준비를 합니다.
- 저장 프로시저(Stored Procedure)나 뷰(View)와 같은 논리적 객체도 SQL 인터페이스를 통해 관리됩니다.
SQL 파서
SQL 문이 MySQL 문법에 맞는지 확인하고, 이를 MySQL 내부에서 처리할 수 있도록 변환하는 과정입니다.
- 토큰화 : SQL 문을 작은 단위(토큰)로 분리합니다.
- 구문 분석 : SQL 문이 MySQL 문법에 맞는지 확인합니다.
- 트리 구조 변환: SQL 문을 실행할 수 있도록 내부적으로 트리 구조로 변환합니다.
SQL 옵티마이저
SQL 옵티마이저는 쿼리를 가장 효율적으로 실행할 수 있도록 최적의 실행 계획을 수립합니다.
- 인덱스를 사용할지 여부를 결정합니다.
- 여러 개의 테이블이 조인될 경우, 어떤 방식(Nested Loop Join, Hash Join 등)으로 조인할지 결정합니다.
- 실행 비용을 평가하여 가장 효율적인 실행 경로를 선택합니다.
캐시 & 버퍼
성능을 높이기 위해 자주 사용하는 데이터나 연산 결과를 메모리에 저장해둡니다.
이렇게 하면 디스크를 직접 읽고 쓰는 횟수를 줄여 속도를 높일 수 있습니다.
- 디스크 I/O를 줄여 성능을 향상합니다.
- 반복적으로 실행되는 연산을 빠르게 처리합니다.
MySQL엔진에 대해서 알아보았습니다. 스토리지 엔진은 무엇일까요?
스토리지 엔진
실제 데이터를 디스크 스토리지에 저장하거나 디스크 스토리지로부터 데이터를 읽어오는 역할을 합니다.
MySQL 엔진이 사람의 머리 역할을 담당했다면, 스토리지 엔진은 사람의 손발 역할을 담당한다고 생각하면 됩니다.
또한, MySQL 엔진은 하나의 엔진을 사용하지만, 스토리지 엔진은 여러 개를 동시에 사용이 가능합니다.
아래와 같은 쿼리문으로 스토리지 엔진의 유형(InnoDB)을 지정할 수 있습니다.
(단, mysql 8.0 버전은 InnoDB가 기본 스토리지 입니다.)
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
age INT
) ENGINE=InnoDB; -- InnoDB로 엔진 설정
스토리지 엔진엔 어떤 종류가 있을까요?
MySQL은 다양한 스토리지 엔진을 지원하며, 각 엔진은 서로 다른 특징을 가지고 있습니다.
대표적인 스토리지 엔진으로는 InnoDB, MyISAM, Memory, CSV, Archive, NDB 등이 있습니다.
이 중 Mysql 8.0의 기본 스토리지 엔진으로 설정된 InnoDB엔진에 대해서만 다뤄보겠습니다.
InnoDB 엔진은 왜 사용할까요?
InnoDB (기본 스토리지 엔진)
InnoDB는 트랜잭션을 지원하고, 동시성 처리 성능이 뛰어나며, 데이터 무결성을 보장하는 등 대부분의 애플리케이션에서 최적의 선택이 됩니다.
InnoDB를 사용하는 주요한 이유는 다음과 같습니다.
- COMMIT, ROLLBACK 기능을 제공하여 데이터 무결성 보장할 수 있는 트랜잭션을 지원합니다.
- 동시 쓰기 작업을 효율적으로 처리 할 수 있는 행 단위 잠금을 지원 합니다.
- 데이터 간 관계를 강제할 수 있는 외래 키를 지원 합니다.
- 예상치 못한 서버 종료 후에도 데이터 손실 방지 하도록 크래시 복구 기능을 제공합니다.(Redo Log, Undo Log)
- 성능 최적화를 위한 고급 캐싱 메커니즘 제공하는 InnoDB 버퍼 풀 기능을 제공합니다.
데이터를 쓰거나 읽을때, 스토리지 엔진에 어떻게 요청을 할까요?
핸들러 API
MySQL 엔진이 데이터를 쓰거나 읽을 때, 스토리지 엔진과 직접 통신하기 위해 핸들러 API를 사용합니다.
핸들러 API는 MySQL 엔진과 스토리지 엔진 간의 인터페이스 역할을 하며, 쿼리 실행 시 스토리지 엔진에 데이터 읽기, 쓰기, 삭제, 인덱스 탐색 등의 요청을 전달합니다.
MySQL엔진이 각 스토리지 엔진에서 데이터를 읽어오거나 저장하도록 명령하기 위해선 반드시 핸들러를 사용해야 합니다.
아래 쿼리문을 사용해서 Mysql 8.0에 존재하는 Handler를 확인해보겠습니다.
show global status like 'Handler%';
쿼리 실행 결과는 다음과 같습니다.
MySQL 구조에 대해서 알아보았습니다. MySQL은 어떤 원리로 동작할까요?
MySQL 동작 원리
MySQL 서버는 프로세스 기반이 아닌 스레드 기반으로 동작합니다.
즉, 클라이언트의 요청을 처리하기 위해 각 요청마다 새로운 프로세스를 생성하는 것이 아니라, 스레드를 생성하여 처리하는 방식입니다.
MySQL에서 스레드는 크게 포그라운드 스레드와 백그라운드 스레드로 구분할 수 있습니다.
아래 쿼리문을 통해서 MySQL 서버에서 실행 중인 스레드의 목록을 확인할 수 있습니다.
select thread_id, name, type, processlist_user, processlist_host from performance_schema.threads order by type, thread_id;
이미지에서 type을 보면 4의 스레드를 제외한 나머지 스레드는 백그라운드 스레드입니다.
또한, thread/sql/one_connection과 같이 이름이 동일한 스레드는 MySQL 서버의 설정 내용으로 인해 여러개의 스레드가 동일한 작업을 병렬로 처리하기 때문입니다.
포그라운드 스레드가 뭘까요?
포그라운드 스레드
포그라운드 스레드는 클라이언트의 요청을 직접 처리하는 스레드를 의미합니다.
즉, 사용자가 SQL 쿼리를 실행하면, 해당 쿼리를 처리하는 전담 스레드가 생성되며, 이 스레드가 포그라운드 스레드입니다.
MySQL은 기본적으로 Thread-Per-Connection(커넥션당 하나의 스레드) 모델을 사용합니다.
즉, 클라이언트가 접속할 때마다 새로운 포그라운드 스레드가 생성됩니다.
포그라운드 스레드는 어떤 흐름으로 동작할까요?
포그라운드 스레드 동작 흐름은 다음과 같습니다.
- 클라이언트가 MySQL 서버에 접속해서 클라이언트와 통신할 포그라운드 스레드를 생성합니다.
- 클라이언트의 SQL 요청(SELECT, INSERT, UPDATE, DELETE)을 처리합니다.
- 쿼리를 실행하고 결과를 반환(SQL 인터페이스 → SQL 파서 → SQL 옵티마이저 → 스토리지 엔진)합니다.
- 커넥션이 종료되면, 스레드도 종료되거나 Thread Pool에 반환됩니다.
포그라운드 스레드는 데이터를 가져올 경우에 먼저 MySQL의 데이터 버퍼나 캐시로부터 가져오고, 버퍼나 캐시에 데이터가 없는 경엔 디스크의 데이터나 인덱스 파일로부터 데이터를 읽어와서 작업을 처리하게 됩니다.
추가로, InnoDB 방식에서는 클라이언트가 데이터를 추가하거나 수정하면, 먼저 메모리(버퍼)에 저장됩니다. 즉, 포그라운드 스레드는 데이터를 즉시 디스크에 기록하지 않고, 임시로 빠른 메모리에 저장합니다. 이후, 일정 시간이 지나거나 데이터가 많아지면 백그라운드 스레드가 이를 실제 디스크에 저장하여 영구적으로 보관합니다.
포그라운드 스레드에 대해 알아봤습니다. 백그라운드 스레드는 뭘까요?
백그라운드 스레드
백그라운드 스레드가 처리하는 작업은 다음과 같습니다.
1. 로그를 디스크로 기록합니다.
- Redo Log(재실행 로그)와 Undo Log(롤백 로그)를 디스크에 저장하여 데이터 무결성을 보장합니다.
- 트랜잭션이 완료될 때마다 로그를 기록하여 장애 발생 시 데이터 복구가 가능하도록 합니다.
2. InnoDB 버퍼 풀(Buffer Pool)의 데이터를 디스크에 기록합니다.
- MySQL은 데이터를 먼저 메모리(버퍼 풀)에 저장한 후, 일정 주기마다 이를 디스크에 기록합니다.
- 백그라운드 스레드는 버퍼 풀의 변경된 페이지를 디스크에 저장하는 플러시(Flush) 작업을 수행합니다.
3. 인서트 버퍼(Insert Buffer)를 병합합니다.
- 인덱스가 설정된 테이블에서 비순차적(Insertion Order가 아닌) 인덱스 업데이트 작업이 발생하면, 이를 바로 반영하지 않고 Insert Buffer에 저장합니다. 이후, 백그라운드 스레드가 여러 개의 변경 사항을 한 번에 병합하여 인덱스 성능을 최적화합니다.
4. 데이터를 버퍼로 읽어옵니다.
- MySQL은 데이터를 디스크에서 바로 읽지 않고, 버퍼 풀에서 먼저 가져오려고 시도합니다.
- 백그라운드 스레드는 미리 데이터를 메모리에 로드하여 성능을 향상합니다.
5. 잠금(Locking)과 데드락(Deadlock)을 모니터링합니다.
- 여러 개의 트랜잭션이 동시에 실행될 때, 잠금 충돌이 발생할 수 있으며, 이는 데드락으로 이어질 수 있습니다.
- 백그라운드 스레드는 이러한 충돌을 감지하고 해결하는 역할을 수행합니다.
인서트 버퍼가 무엇일까요?
인서트 버퍼(Insert Buffer) 는 MySQL InnoDB 엔진에서 보조 인덱스의 쓰기 성능을 최적화하기 위해 사용하는 메커니즘입니다.
쉽게 말해, 비순차적인 인덱스 업데이트를 바로 처리하지 않고, 임시로 저장한 후 한꺼번에 처리하여 성능을 향상하는 기능입니다.
왜 한꺼번해 처리해야 할까요?
데이터를 INSERT 또는 UPDATE 하면 보조 인덱스(Secondary Index)도 함께 갱신해야 합니다.
하지만 보조 인덱스는 데이터가 순차적으로 저장되지 않고, 디스크의 랜덤한 위치에 존재하므로, 이를 갱신할 때 비효율적인 디스크 I/O가 발생합니다. 따라서 변경 사항을 즉시 디스크에 반영하는 대신, Insert Buffer에 저장한 후 일정량이 쌓이면 한 번에 갱신합니다.
만약 Insert Buffer를 사용하지 않고 매번 보조 인덱스를 즉시 갱신한다면,
새로운 데이터가 추가될 때마다 보조 인덱스의 위치를 찾아 개별적으로 업데이트해야 하며, 이 과정에서 디스크 랜덤 읽기(Random Read)가 자주 발생하게 됩니다. 결과적으로, 디스크 접근이 많아지고 성능이 크게 저하될 수 있습니다.
정리
- MySQL은 MySQL 엔진과 스토리지 엔진으로 구성되며, MySQL 엔진이 SQL 쿼리를 해석하고 최적화하는 역할을, 스토리지 엔진이 데이터를 저장하고 조회하는 역할을 담당합니다.
- InnoDB는 MySQL 8.0의 기본 스토리지 엔진으로, 트랜잭션을 지원하고, 행 단위 잠금, 외래 키, 크래시 복구, 버퍼 풀 등의 기능을 제공하여 성능과 데이터 무결성을 보장합니다
- MySQL은 스레드 기반으로 동작하며, 클라이언트의 요청을 처리하는 포그라운드 스레드와 데이터 기록, 캐싱, 잠금 관리 등을 수행하는 백그라운드 스레드로 나뉩니다.
- InnoDB는 성능 최적화를 위해 데이터를 먼저 메모리에 저장한 후, 일정 시간이 지나거나 데이터가 많아지면 백그라운드 스레드가 디스크에 기록하는 방식을 사용합니다.
- Insert Buffer는 비순차적인 보조 인덱스 업데이트를 즉시 처리하지 않고 임시로 저장한 후 한꺼번에 갱신하여 디스크 I/O를 줄이고 성능을 향상시키는 기능입니다.
'Mysql' 카테고리의 다른 글
디스크 I/O 병목 (0) | 2025.04.22 |
---|---|
Master & Slave 구조 (0) | 2025.03.04 |
커버링 인덱스 & 성능 테스트 (0) | 2025.01.30 |
Index와 Query문 조건의 관계 (0) | 2025.01.28 |
[Mysql] 트랜잭션과 잠금 (0) | 2024.12.11 |