우리가 실행하는 SELECT * FROM users라는 단순한 쿼리가 결과로 돌아오기까지, MySQL 내부에서는 어떤 일이 일어나고 있을까요?

MySQL은 단일 프로그램처럼 보이지만, 내부적으로 서버 레이어 와 스토리지 엔진 레이어 로 명확히 분리되어 있습니다. 이 구조를 이해하면 쿼리 최적화, 엔진 선택, 장애 대응까지 훨씬 수월해집니다.

개념 정의

MySQL 아키텍처는 크게 두 층으로 나뉩니다.

  • **서버 레이어 **: 클라이언트 연결 관리, SQL 파싱, 쿼리 최적화, 실행을 담당
  • ** 스토리지 엔진 레이어 **: 실제 데이터의 읽기/쓰기를 담당하는 플러거블 컴포넌트

서버 레이어는 모든 스토리지 엔진이 공유하고, 스토리지 엔진은 테이블 단위로 교체할 수 있습니다. 이것이 MySQL의 ** 플러거블 스토리지 엔진 아키텍처 **입니다.

쿼리 실행의 전체 흐름

클라이언트가 쿼리를 보내면 다음 순서로 처리됩니다.

1. Connection Handler (연결 처리)

PLAINTEXT
클라이언트 → TCP/IP 또는 소켓 → 스레드 할당
  • 클라이언트가 접속하면 MySQL은 전용 ** 스레드 **를 할당합니다
  • 인증(Authentication)과 권한 확인이 이 단계에서 이루어집니다
  • max_connections 설정으로 최대 동시 연결 수를 제어합니다
  • 스레드 풀(Thread Pool)을 사용하면 스레드 생성/소멸 비용을 줄일 수 있습니다

2. Parser (파서)

SQL
SELECT name FROM users WHERE age > 20;
-- Parser가 이 문자열을 파스 트리(Parse Tree)로 변환합니다
  • SQL 문자열을 ** 토큰 **으로 분리합니다 (Lexical Analysis)
  • 토큰을 조합해 ** 파스 트리 **를 생성합니다 (Syntax Analysis)
  • 문법 오류가 있으면 이 단계에서 에러가 발생합니다
  • You have an error in your SQL syntax 에러의 출처가 바로 여기입니다

3. Preprocessor (전처리기)

  • 테이블과 컬럼이 실제로 존재하는지 확인합니다
  • 접근 권한을 검증합니다
  • *를 실제 컬럼 목록으로 확장합니다

4. Optimizer (옵티마이저)

옵티마이저는 MySQL에서 가장 핵심적인 컴포넌트입니다.

  • 비용 기반 최적화(Cost-Based Optimization) 를 사용합니다
  • 여러 실행 계획을 생성하고, 각 계획의 비용을 추정합니다
  • 가장 비용이 낮은 계획을 선택합니다

옵티마이저가 결정하는 것들:

  • 어떤 인덱스를 사용할지
  • 테이블 조인 순서를 어떻게 할지
  • 서브쿼리를 어떻게 변환할지
  • 임시 테이블이 필요한지
SQL
-- EXPLAIN으로 옵티마이저의 선택을 확인할 수 있습니다
EXPLAIN SELECT name FROM users WHERE age > 20;

5. Executor (실행기)

  • 옵티마이저가 만든 실행 계획에 따라 스토리지 엔진 API를 호출합니다
  • 핸들러(Handler) API를 통해 스토리지 엔진과 통신합니다
  • 결과를 클라이언트에 반환합니다
PLAINTEXT
Executor → Handler API → Storage Engine → 디스크/메모리

플러거블 스토리지 엔진 아키텍처

MySQL의 가장 큰 특징은 스토리지 엔진을 ** 플러그인 **처럼 교체할 수 있다는 점입니다.

SQL
-- 테이블 생성 시 엔진 지정
CREATE TABLE logs (
    id INT PRIMARY KEY,
    message TEXT
) ENGINE=InnoDB;

-- 같은 데이터베이스에서 다른 엔진 사용 가능
CREATE TABLE cache_data (
    key_name VARCHAR(100),
    value TEXT
) ENGINE=MEMORY;

-- 현재 지원하는 엔진 확인
SHOW ENGINES;

스토리지 엔진은 서버 레이어와 Handler API 를 통해 소통합니다. 주요 API는 다음과 같습니다.

API역할
ha_open()테이블 열기
ha_read_first()첫 번째 행 읽기
ha_read_next()다음 행 읽기
ha_write_row()행 쓰기
ha_delete_row()행 삭제

InnoDB vs MyISAM

MySQL 8.0부터 InnoDB가 기본 엔진이지만, 차이를 이해하는 것은 여전히 중요합니다.

특성InnoDBMyISAM
트랜잭션O (ACID 지원)X
잠금 수준행(Row) 수준테이블(Table) 수준
외래 키OX
크래시 복구O (Redo/Undo Log)제한적
전문 검색O (5.6+)O
공간 인덱스O (5.7+)O
캐시Buffer PoolKey Cache (인덱스만)
저장 방식클러스터형 인덱스힙 구조

MyISAM은 언제 쓸까?

사실 MySQL 8.0 기준으로 MyISAM을 선택할 이유는 거의 없습니다. 하지만 레거시 시스템에서 여전히 사용되는 경우가 있습니다.

  • 읽기 전용 데이터 (로그 아카이브 등)
  • 트랜잭션이 전혀 필요 없는 경우
  • COUNT(*)가 매우 빈번한 경우 (MyISAM은 행 수를 별도 저장)

그 외 스토리지 엔진

엔진특징
MEMORY메모리에만 저장, 재시작 시 데이터 소실
CSVCSV 파일 형식으로 저장
Archive압축 저장, INSERT/SELECT만 지원
NDB(Cluster)MySQL Cluster용 분산 엔진
Federated원격 MySQL 테이블 접근

Query Cache — 왜 사라졌을까

MySQL 5.7까지 존재했던 Query Cache는 8.0에서 완전히 제거되었습니다.

PLAINTEXT
쿼리 → Query Cache 확인 → 캐시 히트 시 바로 반환
                        → 캐시 미스 시 정상 실행

제거된 이유:

  • **동시성 병목 **: 캐시 접근 시 전역 뮤텍스 필요
  • ** 무효화 비용 **: 테이블에 한 행만 변경되어도 해당 테이블의 모든 캐시가 무효화
  • ** 메모리 비효율 **: 쿼리 문자열이 한 글자만 달라도 별개의 캐시 항목

대안으로는 ** 애플리케이션 레벨 캐시 **(Redis, Memcached)나 ProxySQL 같은 프록시 레이어 캐시를 사용합니다.

서버 레이어의 주요 구성 요소 정리

PLAINTEXT
┌─────────────────────────────────────────┐
│           클라이언트 연결                   │
├─────────────────────────────────────────┤
│  Connection Handler  (스레드 할당, 인증)    │
├─────────────────────────────────────────┤
│  Parser             (SQL → 파스 트리)      │
├─────────────────────────────────────────┤
│  Preprocessor       (권한/존재 여부 확인)    │
├─────────────────────────────────────────┤
│  Optimizer          (실행 계획 수립)        │
├─────────────────────────────────────────┤
│  Executor           (Handler API 호출)     │
├─────────────────────────────────────────┤
│         Storage Engine (InnoDB 등)         │
├─────────────────────────────────────────┤
│              디스크 / 메모리                 │
└─────────────────────────────────────────┘

실무에서 확인하는 방법

SQL
-- 현재 연결 상태 확인
SHOW PROCESSLIST;

-- 서버 상태 변수 확인
SHOW GLOBAL STATUS LIKE 'Threads%';

-- 테이블별 사용 엔진 확인
SELECT TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'my_database';

-- 옵티마이저가 선택한 실행 계획 확인
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE id = 1;

주의할 점

Query Cache 제거를 모르면 잘못된 최적화를 할 수 있다

MySQL 5.7에서 8.0으로 마이그레이션할 때, Query Cache 관련 설정(query_cache_size, query_cache_type)이 남아 있으면 서버가 시작되지 않거나 경고가 발생합니다. 8.0에서는 이 설정을 완전히 제거해야 합니다.

스토리지 엔진을 혼용하면 트랜잭션이 깨질 수 있다

InnoDB 테이블과 MyISAM 테이블을 하나의 트랜잭션 안에서 함께 수정하면, MyISAM은 롤백되지 않습니다. MyISAM은 트랜잭션을 지원하지 않기 때문입니다.

옵티마이저의 판단이 항상 최적은 아니다

옵티마이저는 통계 정보 기반으로 판단하므로, 통계가 실제 데이터와 다르면 비효율적인 실행 계획을 세울 수 있습니다. ANALYZE TABLE로 통계를 갱신하거나, EXPLAIN으로 실행 계획을 검증하는 습관이 중요합니다.

정리

항목설명
서버 레이어파싱, 최적화, 실행을 담당하며 모든 엔진이 공유
스토리지 엔진데이터 읽기/쓰기 담당, 테이블 단위로 교체 가능
쿼리 흐름Connection → Parser → Preprocessor → Optimizer → Executor → Engine
InnoDBMySQL 8.0 기본 엔진, 트랜잭션/행 잠금/크래시 복구 지원
Query Cache8.0에서 완전 제거, 동시성 병목과 무효화 비용 때문
핵심 도구EXPLAIN으로 옵티마이저의 실행 계획을 확인
댓글 로딩 중...