본문 바로가기
Study OR Book/Book

[Real Mysql] 4장

by Baest 2025. 9. 2.

 

 

 

 

엔진

엔진에는 크게 두 가지가 있다.

  1. MySQL 엔진
    1. 클라이언트로부터의 접속 및 쿼리 요청을 처리하는 커넥션 핸들러
    2. SQL 파서 및 전처리기, 쿼리의 최적화된 실행을 위한 옵티마이저가 중심
  2. 스토리지 엔진
    1. 실제 데이터를 디스크 스토리지에 저장하거나 디스크 스토리지로부터 데이터를 읽어오는 부분 전담
    2. MySQL 서버에서 MySQL 엔진은 하나지만 스토리지 엔진은 여러 개를 동시에 사용할 수 있음

스레딩 구조

MySQL 서버는 프로세스 기반이 아닌 스레드 기반으로 작동하며, 크게 포그라운드와 백그라운드 스레드로 구분 가능하다.

 

  1. 포그라운드 스레드(클라이언트 스레드)
    1. 포그라운드 스레드는 MySQL 서버에 접속된 클라이언트 수만큼 존재 (최소)
    2. 주된 역할: 각 클라이언트 사용자가 요청하는 쿼리 문장 처리
    3. 포그라운드 스레드는 데이터를 MySQL의 데이터 버퍼나 캐시로부터 가져오고, 여기 없다면 직접 디스크의 데이터나 인덱스 파일로부터 데이터를 읽어와서 작업을 처리
    4. InnoDB 테이블의 경우 데이터 버퍼나 캐시까지만 포그라운드 스레드가 처리하고, 나머지 버퍼로부터 디스크까지 기록하는 작업은 백그라운드 스레드가 처리
    5. 참고로 사용자 스레드와 포그라운드 스레드는 같은 것을 의미하는데, 클라이언트와 통신하기 때문에 포그라운드 스레드라고 불리며 사용자가 요청한 작업을 처리하기 때문에 사용자 스레드라고도 불림

참고로 spring yaml 파일의 DB 셋팅은 포그라운드 스레드에 영향을 줌

# application.yml - 포그라운드 스레드에 영향을 주는 설정
spring:
  datasource:
    hikari:
      maximum-pool-size: 20        # 최대 포그라운드 스레드 수 결정
      connection-timeout: 30000    # 포그라운드 스레드 대기 시간
      validation-timeout: 5000     # 연결 검증 시간
      leak-detection-threshold: 60000  # 연결 누수 감지

 

  1. 백그라운드 스레드
    1. MyISAM은 해당 사항 없고, InnoDB에만 해당
    2. InnoDB에서 여러 작업이 백그라운드로 처리
      1. 인서트 버퍼를 병합하는 스레드
      2. 로그를 디스크로 기록하는 스레드
      3. InnoDB 버퍼 풀의 데이터를 디스크에 기록하는 스레드
      4. 데이터를 버퍼로 읽어 오는 스레드
      5. 잠금이나 데드락을 모니터링하는 스레드
    3. 사용자의 요청 처리 도중 데이터의 쓰기 작업은 지연 처리 될 수 있으나 읽기 작업은 절대 지연될 수 없음
      1. 상용 DBMS에서 쓰기 작업 일괄 처리 기능 탑재
      2. InnoDB도 이런 방식으로 처리
      3. INSERT, UPDATE, DELETE 쿼리로 데이터 변경 시 데이터가 디스크의 데이터 파일로 완전히 저장될 때까지 기다려되지 않는 이유

메모리 할당 및 사용 구조

크게 글로벌과 로컬 메모리 영역으로 구분된다. 글로벌 메모리 영역은 MySQL 서버가 시작되면서 운영체제로부터 할당된다. 운영체제 마다 메모리 할당 방식이 다른데, 복잡하기 때문에 MySQL의 시스템 변수로 설정해 둔 만큼 메모리를 할당받는다고 생각하면 된다.

  1. 글로벌 메모리 영역 일반적으로 클라이언트 스레드의 수와 무관하게 하나의 메모리 공간만 할당된다. (필요에 따라 2개 이상 공간 할당 가능)
    1. 테이블 캐시
    2. InnoDB 버퍼 풀
    3. InnoDB 어댑티브 해시 인덱스
    4. InnoDB 리두 로그 버퍼
  2. 로컬 메모리 영역 세션 메모리 영역이라고도 표현되고, MySQL 서버상 존재하는 클라이언트 스레드가 쿼리를 처리하는데 사용된다. 클라이언트가 MySQL 서버에 접속하면 MySQL 서버에서는 클라이언트의 커넥션으로부터의 요청을 처리하기 위해 스레드를 하나씩 할당한다. 로컬 메모리는 각 클라이언트 스레드별로 독립적으로 할당되며 절대 공유되지 않는다.
    1. 정렬(소트) 버퍼: 쿼리 실행 순간에만 할당 후 다시 해제
    2. 조인 버퍼: 쿼리 실행 순간에만 할당 후 다시 해제
    3. 바이너리 로그 캐시
    4. 네트워크 버퍼

플러그인 스토리지 엔진 모델

MySQL의 독특한 구조 중 대표적인 것이 플러그인 모델이다. 플러그인해서 사용할 수 있는 것은 스토리지 엔진, 컴포넌트, 플러그인 등이 있다.

 

데이터 읽기/쓰기 작업은 대부분 1건의 레코드 단위로 처리된다. MySQL을 사용하다보면, 핸들러라는 단어를 자주 접하게 된다. 핸들러는 MySQL 서버의 소스코드로부터 넘어온 표현이다. MySQL 엔진이 스토리지 엔진을 조정하기 위해 핸들러라는 것을 사용하게 된다.

개념이 쉽지는 않아서 MySQL 엔진이 각 스토리지 엔진에게 데이터를 읽어오거나 저장하도록 명령하려면 반드시 핸들러를 통해야 한다는 점만 기억하면 된다.

 

중요한 내용은

하나의 쿼리 작업은 여러 하위 작업으로 나뉘는데, 각 하위 작업이 MySQL 엔진 영역에서 처리되는지 아니면 스토리지 엔진 영역에서 처리되는지 구분할 줄 알아야한다.

 

 

쿼리 실행 구조

쿼리 실행 관점에서 아래와 같이 기능별로 나눌 수 있다.

  1. 쿼리 파서
    역할: 사용자 요청으로 들어온 쿼리 문장을 토큰으로 분리해 트리 형태의 구조로 생성
    결과: 쿼리 문법 오류는 이 과정에서 발견 후 사용자에게 오류 메세지를 전달
  2. 전처리기
    역할: 파서 과정에서 만들어진 파서 트리 기반으로 쿼리 문장에 구조적 문제점 검토
    토큰을 테이블 이름, 컬럼 이름, 내장 함수와 같은 개체와 매핑해 객체 존재 여부 및 접근 권한 확인
    결과: 존재하지 않거나 권한 이슈가 있을 경우 이 단계에서 걸러짐
  3. 옵티마이저
    역할: 사용자 쿼리를 저렴한 비용으로 빠르게 처리할지 결정 (DBMS의 두뇌 역할)
    결과: 옵티마이저에 의한 어떤 선택(책에서 옵티마이저가 더 나은 선택을 하도록 유도하는지 다루고 있음)
  4. 실행엔진
    역할: 만들어진 계획대로 각 핸들러에게 요청해서 받은 결과를 또 다른 핸들러의 요청의 입력으로 연결 (DBMS의 손과 발 역할)
    결과: 앞선 핸들러들의 처리 결과를 사용자나 다른 모듈로 넘김
  5. 핸들러 (스토리지 엔진)
    역할: MySQL 서버의 가장 밑단에서 MySQL 실행 엔진의 요청에 따라 데이터를 디스크로 저장하고 읽어 오는 역할
    즉, 핸들러는 스토리지 엔진을 의미함

 

쿼리 캐시

MySQL 8.0 이전

SQL의 실행 결과를 메모리에 캐시하고, 동일 SQL 쿼리가 실행되면 테이블을 읽지 않고 즉시 결과를 반환했다.

하지만 테이블 데이터가 변경되는 경우 기존 캐시 데이터를 삭제하는 과정에서 심각한 성능 저하를 유발했다.

 

MySQL 8.0

MySQL 서버 기능에서 완전 제거되었다.

 

 

스레드 풀

전통적인 방식(one-thread-per-connection)

- 각 클라이언트 연결마다 별도 스레드 생성

- 연결 수 증가하면 스레드 수도 증가

- 많은 연결 시 컨텍스트 스위칭 오버헤드 발생

- 메모리 사용량 증가 (각 스레드당 기본 256KB 스택)

 

스레드 풀 방식

- 고정된 수의 워커 스레드로 모든 연결 처리

- 연결과 스레드가 분리

- 높은 동시성 상황에서 성능 향상

- 메모리 사용량 예측 가능

 

Percona Server

MySQL의 오픈소스 포크로 MySQL과 완전 호환되며 추가적인 성능 개선과 기능을 제공하는 데이터베이스

 

주요특징

1. 향상된 스레드 풀
- MySQL Enterprice 스레드 풀보다 더 정교한 구현

- 자동 스케일링 기능

- 더 나은 우선순위 처리


2. 성능 개선

- 향상된 InnoDB 버퍼 풀 관리 (innodb_buffer_pool_dump_pct = 40)

- 개선된 플러시 알고리즘 (innodb_adaptive_flushing_lwm = 10)

- 향상된 압축 (innodb_compression_default = ON)

 

Percona Server 선택 기준

- 높은 동시 연결 수가 필요한 환경

- 상세한 성능 모니터링이 필요한 경우

- 데이터 압축이 중요한 환경

- 무료로 엔터프라이즈급 기능이 필요한 경우