Changsoon's Note Backend Developer

MySQL 아키텍처

alt text

MySQL 서버는 크게 MySQL 엔진과 스토리지 엔진으로 구분할 수 있다.

MySQL 엔진

MySQL 엔진은 커넥션 핸들러, SQL 파서 및 전처리기, 옵티마이저가 중심을 이룬다.

  • 커넥션 핸들러 : 클라이언트로부터의 접속 및 쿼리 요청을 처리
  • 옵티마이저 : 쿼리의 최적화된 실행

스토리지 엔진

실제 데이터를 디스크 스토리지에 저장하거나 디스크 스토리지로부터 데이터를 읽어오는 부분은 스토리지 엔진이 담당한다.

MySQL 서버에서 MySQL 엔진은 하나지만 스토리지 엔진은 여러 개를 동시에 사용할 수 있다.

MySQL 스레딩 구조

alt text

MySQL 서버는 프로세스 기반이 아니라 스레드 기반으로 작동하며 크게 포그라운드 스레드와 백그라운드 스레드로 구분할 수 있다.

  • 포그라운드 스레드(클라이언트 스레드)
    • 데이터를 MySQL의 데이터 버퍼나 캐시로부터 가져오며, 버퍼나 캐시에 없는 경우에는 직접 디스크의 데이터나 인덱스 파일로부터 데이터를 읽어와서 작업을 처리한다.
    • 포그라운드 스레드는 최소한 MySQL 서버에 접속된 클라이언트 수만큼 존재하며, 주로 각 클라이언트 사용자가 요청하는 쿼리 문장을 처리한다.
  • 백그라운드 스레드
    • InnoDB는 여러 가지 작업이 백그라운드로 처리된다.
    • Insert 버퍼를 병합하는 스레드
    • 로그를 디스크로 기록하는 스레드
    • 데이터를 버퍼로 읽어오는 스레드
    • 잠금이나 데드락을 모니터링하는 스레드

메모리 할당 및 사용 구조

alt text

MySQL에서 사용되는 메모리 공간은 크게 글로벌 메모리 영역과 로컬 메모리 영역으로 구분할 수 있다.

  • 글로벌 메모리 영역
    • 글로벌 메모리 영역의 모든 메모리 공간은 MySQL 서버가 시작되면서 운영체제로부터 할당된다.
    • 일반적으로 클라이언트 스레드의 수와 무관하게 하나의 메모리 공간만 할당된다.
  • 로컬 메모리 영역
    • MySQL 서버상에 존재하는 클라이언트 스레드가 쿼리를 처리하는 데 사용하는 메모리 영역이다.
    • 로컬 메모리는 각 클라이언트 스레드별로 독립적으로 할당되며 절대 공유되어 사용되지 않는다.

쿼리 실행 구조

alt text

  • 쿼리 파서
    • 쿼리 파서는 사용자 요청으로 들어온 쿼리 문장을 토큰으로 분리해 트리 형태의 구조로 만들어 내는 작업을 의미한다.
    • 쿼리 문장의 기본 문법 오류는 이 과정에서 발견되고 전달된다.
  • 전처리기
    • 파서 과정에서 만들어진 파서 트리를 기반으로 쿼리 문장에 구조적인 문제점을 확인한다.
    • 객체의 존재 여부와 객체의 접근 권한 등을 확인하는 과정을 수행한다.
  • 옵티마이저
    • DBMS의 두뇌, 사용자의 요청으로 들어온 쿼리 문장을 낮은 비용으로 빠르게 처리할지를 결정한다.
  • 실행 엔진
    • 실행 엔진과 핸들러는 손과 발이다.
    • 실행 엔진은 만들어진 계획대로 각 핸들러에게 요청해서 받은 결과를 또 다른 핸들러 요청의 입력으로 연결하는 역할을 수핸한다.
  • 핸들러(스토리지 엔진)
    • MySQL 서버의 가장 밑단에서 MySQL 실행 엔진의 요청에 따라 데이터를 디스크로 저장하고 디스크로부터 읽어오는 역할을 담당한다.

InnoDB 스토리지 엔진 아키텍처

alt text

InnoDB는 MySQL 스토리지 엔진으로 레코드 기반의 잠금을 제공하며, 높은 동시성 처리를 할 수 있고 안정적이며 성능이 뛰어나다.

  • InnoDB의 모든 테이블은 기본적으로 프라이머리 키를 기준으로 클러스터링되어 저장된다.
  • InnoDB 스토리지 엔진 레벨에서 외래 키에 대한 지원을 한다.
  • MVCC(Multi Version Concurrency Control)
    • 일반적으로 레코드 레벨의 트랜잭션을 지원하는 기능이며, MVCC의 가장 큰 목적은 잠금을 사용하지 않는 일관된 읽기를 제공하는 데 있다.
    • InnoDB는 언두 로그를 이용해 이 기능을 구현한다.
  • 잠금 없는 일관된 읽기
    • InnoDB 스토리지 엔진은 MVCC 기술을 이용해 잠금을 걸지 않고 읽기 작업을 수행한다.
    • 잠금을 걸지 않기 때문에 InnoDB에서 읽기 작업은 다른 트랜잭션이 가지고 있는 잠금을 기다리지 않고, 읽기 작업이 가능하다.
  • 자동 데드락 감지
    • InnoDB 스토리지 엔진은 내부적으로 잠금이 교착 상태에 빠지지 않았는지 체크한다.
  • InnoDB에는 손실이나 장애로부터 데이터를 보호하기 위한 메커니즘이 탑재돼 있다.

언두로그

언두로그는 트랜잭션과 격리 수준을 보장하기 위해 DML로 변경되기 이전 버전의 데이터를 별도로 백업하는 것을 말한다.

언두로그는 트랜잭션과 격리 수준을 보장해준다.

리두로그

리두 로그는 영속성과 관련이 있는데, MySQL 서버가 비정상적으로 종료 됐을 때 데이터 파일에 기록하지 못한 데이터를 잃지 않게 해주는 안전장치다.

InnoDB 버퍼 풀

InnoDB 스토리지 엔진에서 버퍼 풀은 가장 핵심적인 부분으로, 디스크의 데이터 파일이나 인덱스 정보를 메모리에 캐시해두는 공간이다.

쓰기 작업을 지연시켜 일괄 작업으로 처리할 수 있게 해주는 버퍼 역할도 같이 한다.

  • 버퍼 풀의 구조
    • InnoDB 스토리지 엔진은 버퍼 풀이라는 거대한 메모리 공간을 페이지 크기의 조각으로 쪼개어 InnoDB 스토리지 엔진이 데이터를 필요로 할 때 해당 데이터 페이지를 읽어서 각 조각에 저장한다.
    • 버퍼 풀의 페이지 크기 조각을 관리하기 위해 InnoDB 스토리지 엔진은 크게 LRU 리스트와 플러시 리스트, 프리 리스트라는 3가지 자료 구조를 관리한다.
  • InnoDB 스토리지 엔진에서 데이터를 찾는 과정
    • 필요한 레코드가 저장된 데이터 페이지가 버퍼 풀에 있는지 검사
    • 디스크에서 필요한 데이터 페이지를 버퍼 풀에 적재하고, 적재된 페이지에 대한 포인터를 LRU 헤더 부분에 추가
    • 버퍼풀의 LRU 헤더 부분에 적재된 데이터 페이지가 실제로 읽히면 MRU (Most Recently Used) 헤더 부분으로 이동
    • 버퍼풀에 상주하는 데이터 페이지는 사용자 쿼리가 얼마나 접근했었는지에 따라 나이가 부여되며, 나이가 오래되면 해당 페이지는 버퍼 풀에서 제거됨
    • 버퍼 풀의 데이터 페이지가 쿼리에 의해 사용되면 나이가 초기화되고 MRU의 헤더 부분으로 옮겨진다.
  • 버퍼풀과 리두 로그
    • InnoDB 버퍼 풀과 리두 로그는 매우 밀접한 관계를 갖는다.
    • InnoDB 버퍼 풀은 서버의 메모리가 혀용하는 만큼 크게 설정할수록 쿼리의 성능이 빨라진다.
    • DB 성능 향상을 위해 데이터 캐시와 쓰기 버퍼링이라는 두 가지 방법이 있는데, 버퍼 풀은 데이터 캐시 기능만 향상시키는 것이다.
  • 버퍼 풀 플러시
    • InnoDB 스톨리지 엔진은 버퍼 풀에서 아직 디스크로 기록되지 않은 더티 페이지들을 성능상의 악영향 없이 디스크에 동기화하기 위해 2개의 플러시 기능을 백그라운드로 실행한다.
    • 플러시 리스트 플러시
      • 리두 로그 공간의 재활용을 위해 오래된 리두 로그 공간을 비워줘야 한다.
      • 이때 공간을 지우려면 반드시 InnoDB 버퍼 풀의 더티 페이지가 먼저 디스크로 동기화돼야 한다.
      • 이를 위해 플러시 리스트 플러시 함수를 호출해서 오래전에 변경된 데이터 페이지 순서대로 디스크에 동기화하는 작업을 수행한다.
    • LRU 리스트 플러시
      • InnoDB 스토리지 엔진은 LRU 리스트에서 사용 빈도가 낮은 데이터 페이지들을 제거한다.

Double Wirte Buffer

InnoDB의 리두 로그는 리두 로그 공간의 낭비를 막기 위해 페이지의 변경된 내용만 기록한다.

InnoDB 스토리지 엔진에서는 이러한 문제를 막기 위해 Double-Write 기법을 이용한다.

InnoDB 스토리지 엔진은 실제 데이터 파일에 변경 내용을 기록하기 전에 더티 페이지를 우선 묶어 한 번의 디스크 쓰기로 시스템 테이블스페이스의 DoubleWrite 버퍼에 기록하고 각 더티 페이지를 파일의 적당한 위치에 하나씩 랜덤으로 쓰기를 실행한다.

체인지 버퍼

InnoDB는 변경해야 할 인덱스 페이지가 버퍼 풀에 있으면 바로 업데이트를 하지만 디스크로부터 읽어와서 업데이트해야 한다면 임시 공간에 저장해 두고 바로 사용자에게 결과를 반환하는 방식으로 성능을 높인다.

이때 사용하는 임시 메모리 공간을 체인지 버퍼라고 한다.