- Published on
Real MySQL 아키텍처 개인적인 정리
- Authors
- Name
- 김민석
Introduction
Real MySQL 아키텍처

크게 4가지로 구분 해볼 수 있다.
- MySQL 접속 클라이언
- MySQL 엔진
- MySQL 스토리지 엔진
- 운영체제, 하드웨어
MySQL 접속 클라이언
- MySQL은 대부분
프로그래밍 언어
에 대해 접속 API를 제공한다. - 쉘에서도
쉘 스크립트
를 통해 접속이 가능하다.
MySQL 엔진
MySQL의 두뇌
를 담당하는 곳이다.
- 클라이언트 접속과 SQL 요청을 처리.
쿼리 파서
,전처리기
,옵티마이저
,쿼리 실행
등으로 이루져 있다.- (옵티마이저가 중요) MySQL 엔진을 두뇌라고 비유한 이유는 옵티마이저 때문이다.
"옵티마이저란?"
- 요청된 SQL문을 최적화해서 실행시키기 위해 실행 계획을 짜는 중요한 역할 한다.
- 쿼리 변환, 비용 최적화, 실행 계획 수립
MySQL 스토리지 엔진
MySQL의 손발
역할을 담당하는 곳이다.
- 데이터를 실제로
디스크에 저장
하거나, 디스크에 저장된데이터를 읽어오는 역할
을 한다. 옵티마이저가 작성한 실행 계획에 따라서
스토리지 엔진을 호출해서쿼리
를 실행한다.
핸들러 API
- MySQL 엔진이 스토리지를 호출할때 사용하는 API를
핸들러 API
라고 한다. - 또한 핸들러 API를 직접 구현해서 나만의 스토리지 엔진을 추가할 수도 있다.
운영체제, 하드웨어
- 마지막으로 MySQL 실제 테이블의 데이터와 로그 데이터를 파일로 저장하는
운영체제
,하드웨어
로 나눠 볼 수 있다.
쿼리 실행 과정

쿼리 캐시
- SQL 실행 결과를 메모리에 캐싱하는 역할
- 사용자가 SQL 요청을 MySQL로 보내면
가장 먼저 쿼리 캐시를 만난다.
- 쿼리 캐시는 쿼리 요청 결과를
캐싱하는 모듈
이다.
- 사용자가 SQL 요청을 MySQL로 보내면
- 동일 SQL 실행시 이전 결과 즉시 반환
- 쿼리 캐시를 통해서 동일한 SQL 요청에 대한 결과를
즉시 빠르게
받을 수 있다.
- 쿼리 캐시를 통해서 동일한 SQL 요청에 대한 결과를
- 테이블의 데이터가 변경되면 캐싱된 데이터 삭제 필요
- 하지만 쿼리 캐시는 캐싱하고 있는 데이터의 테이블이 변경 된다면
더 이상 쓸모 없어지게 된 캐싱 데이터를 삭제
해야 한다. - 캐싱 데이터가 삭제 될때마다 쿼리 캐시에 접급하는
쓰레드에 Lock
이 걸리는데, 이는 심각한동시 처리 성능 저하
를 유발한다.
- 하지만 쿼리 캐시는 캐싱하고 있는 데이터의 테이블이 변경 된다면
- MySQL 8.0 부터 완전히 제거
MySQL 8.0
부터는 쿼리 캐시가완전히 삭제 되었다.
쿼리 파서
쿼리 파서는 기본적인 SQL 문장 오류를 체크
한다.
그리고 SQL 문장을 의미있는 단위의 토큰
으로 쪼갠 다음에 트리로 만든다.
- 트리를
Parse Tree
라고 부른다. - MySQL은 내부적으로 Parse Tree를 사용해서
쿼리를 실행
한다.
전처리기
쿼리 파서가 만든 Parse Tree를 기반으로 쿼리 문장에 구조적인 문제점이 있는지 검사
한다.
- 파스 트리의 토큰을 하나씩 검사하면서, 토큰에 해당하는
테이블 이름
이나컬럼
등이실제로 존재하는 값인지 체크
하고,접근 권한
에 대해서도 체크 한다.
옵티마이저
옵티마이저는 SQL을 최적화
해서 실행 시키는 쿼리 실행 계획
을 만든다.
- 옵티마이저가 SQL을 최적화 하는 방법은 2가지가 있다.
‘규칙 기반 최적화’
와‘비용 기반 최적화’
- 규칙 기반 최적화: 옵티마이저에 내장된
우선 순위에 따라 점수를 매겨
실행 계획을 수립하는 방법이다. - 비용 기반 최적화: SQL을 처리하는 다양한 방법을 마련해두고 각 방법의
비용과 테이블 통계 정보
를 통해서 실행 계획을 수립하는 방법이다.
- 규칙 기반 최적화: 옵티마이저에 내장된
쿼리 실행 엔진
- 옵티마이저가 만들어준 쿼리 실행 계획에 따라서 스토리지 엔진을 적절히 호출해서
쿼리를 수행
하는 역할을 담당한다.
옵티마이저가 만든 실행 계획대로 스토리지 엔진을 호출해서 레코드를 읽고 쓴다
스토리지 엔진
쿼리 실행 엔진이 요청한대로, 데이터를 디스크에 저장
하거나, 디스크로부터 데이터를 읽어
오는데, 대표적으로 InnoDB
, MyISAM
스토리지 엔진이 있다.
- 이 스토리지 엔진은 플러그인 형태로 제공되기 때문에 사용자는 원하는 스토리지 엔진을 선택해서 사용할 수 있다.
InnoDB 스토리지 엔진

크게 3가지로 구분 해볼 수 있다.
- 프라이머리(PK) 키에 의한 클러스터링
- 트랜잭션 지원
- MVCC, 리두 로그 & 언두 로그, 레코드 단위 잠금
- InnoDB 버퍼풀 & 어댑티브 해시 인덱스
프라이머리 키에 의한 클러스터링

프라이머리 키를 기준으로 데이터를 묶어서 저장
하는 것이다.
- PK 순서대로 레코드를
정렬
해서디스크에 저장
하는 것을 뜻한다. - 참고로 InnoDB 에서는
PK를 통해서 데이터 파일에 접급
하며, PK에 대한인덱스를 자동으로 생성
해주기도 한다. - 클러스터링을 하면 PK 기반 범위
검색 속도가 매우 빨라진다
. 이유는 PK 기준으로 데이터가정렬되어서 묶여서 한 군데 저장
되어있기 때문이다. - 하지만
쓰기 성능은 저하 된다
. PK 값이 바뀌면 그에 따라 레코드의물리적 순서도 하나씩 바꿔줘야 하기 때문
이다. - 웹 서비스는 쓰기보다 읽기 요청이 훨씬 많기 때문에
쓰기 성능을 희생
하고읽기 성능을 얻는 클러스터링을 하는 것이 합리적
이기 때문이다.
- 레코드를 PK순으로 정렬해서 저장
- PK 인덱스 자동 생성
- PK를 통해서만 레코드에 접근 가능
- PK를 통한 범위 검색이 매우 빠름
- But 클러스터링 때문에 쓰기 성능 저하
트랜잭션 - MVCC(다양한 버전이 동시에 관리된다)

InnoDB는 기본적으로 Commit
, Rollback
기능을 제공한다. 그리고 MVCC
라는 기능도 제공한다.
- 위 그림을 보면
- InnoDB 버퍼풀은 변경된 데이터를 디스크에
반영하기전까지 잠시 버퍼링 하는 공간
이다. - 언두 로그는 변경되기 이전 데이터를
백업 해두는 공간
이라고 보면 된다.
- InnoDB 버퍼풀은 변경된 데이터를 디스크에
user 라는 테이블에
id는 1번, 이름은 '김민석', 지역은 '부산'인
레코드를 삽입하는 쿼리를 실행하고 커밋을 찍어본다.
- 그러면
버퍼 풀
에 새로 삽입한 레코드가 생긴다.

- 이 상태에서
‘김민석’
의 지역을‘서울’
로 바꾸는update 쿼리
를 날리면 버퍼풀에 있는‘김민석’
레코드의 지역은 바뀌고 이전‘부산’
이였던 컬럼 값은id
와 함께언두로그에 복사
되는 것이다.

- 이 상태에서
다른 트랜잭션
이‘김민석’
의 레코드를 조회한다면, 어떤 결과를 반환할까?- 이건
트랜잭션 격리 수준
에 따라 다르다.READ_UNCOMMITTED
라면 버퍼풀에 있는 것이 조회되고,- 나머지 격리 수준이라면
언두 로그
에 있는 데이터가 조회 되는 것이다.
- 이건
언두 로그 & 리두 로그
- 언두 로그
- 변경되기 이전 데이터를
백업해서 트랜잭션을 보장
한다.- 롤백을 한다면 언두 로그에 백업된 데이터로 그대로
복원
해주면 되는 것이다.
- 롤백을 한다면 언두 로그에 백업된 데이터로 그대로
- 트랜잭션 격시 수준도 보장해준다.
- 트랜잭션 격리 수준에 따라 언두 로그에 있는
데이터를 반환
해준다.
- 트랜잭션 격리 수준에 따라 언두 로그에 있는
- 변경되기 이전 데이터를
- 리두 로그
- 리두 로그는 트랜잭션의
영속성을 보장
해준다.서버 비정상 종료시
, 리두 로그에백업된 데이터 복원
한다.
변경된
데이터,commit
된 데이터를 백업 한다.
- 리두 로그는 트랜잭션의
레코드 단위 잠금
- 데이터 베이스에서 데이터를 변경할때는
동시성 문제
를 고려해서 레코드에 대한 접근을 막는데, 이걸잠금
이라고 한다. - InnoDB는 레코드 단위로 잠금을 걸기 때문에
동시처리 성능이 좋다
- 실제로는 레코드 그 자체를 잠그는 것이 아니라,
인덱스 레코드를 잠그는 것이다.
- 실제로는 레코드 그 자체를 잠그는 것이 아니라,
- 레코드 단위로 잠금을 걸기 때문에 동시 처리 성능이 좋다.
- 사실 레코드 자체를 잠그는 것이 아니라, 인덱스를 잠근다.
레코드 단위 잠금 - 예시

성별은 ‘남자’
이름은 ‘김민석’
이라는 레코드의 운동 컬럼을
‘축구’
에서 ‘달리기’
로 바꾸는 update
쿼리를 날리면 그림 처럼 ‘김민석’
레코드만 잠기지 않는다.

InnoDB는 레코드 그 자체를 잠그지 않고, 인덱스 레코드를 잠그는 방식으로 처리
되기 때문이다.
- 업데이트 할 레코드를 검색할때, 사용된
인덱스 레코드가 잠긴다.
- 그림처럼 검색에 사용된 성별 인덱스
레코드가 모두 잠기게
되는 것이다.

만약 성별 인덱스가 없었다면?
‘김민석’
레코드를 찾기 위해서 기본으로 생성된 PK 인덱스를 사용하여 테이블 풀 스캔
한다.
- 이 경우에도 검색에 사용된
모든 PK 인덱스가 잠긴다.
- 전체의 레코드가 100개라고 한다면 100개의 레코드가
전부 잠기게 되는 것이다.
하지만. 복합 인덱스를 생성했다면, 내가 원하는 김민석 레코드만 잠글 수 있다.
InnoDB 스토리지 엔진 특징 - 버퍼풀

버퍼풀의 용도 2가지
- 데이터 캐싱
- 쓰기 지연 버퍼
데이터 캐싱
버퍼풀은 디스크의 데이터 파일이나 인덱스 정보를 메모리에 캐싱해두는 공간
이다.
- 버퍼풀은
쓰기 작업을 지연
시켜서 일괄적으로 작업을 처리해주기도 한다. - 버퍼풀은 SQL 요청 결과를
일정한 크기의 페이지 단위로 캐싱
하는데, 운영체제가가상 메모리를 효율적으로 사용하기 위해
페이징을 하는 것 처럼데이터베이스도 테이블 데이터에 대해 페이징
을 한다.- InnoDB는 페이지 교체 알고리즘으로
LRU 알고리즘
을 사용하고 있다.
- InnoDB는 페이지 교체 알고리즘으로
쓰기 지연 버퍼
insert
,update
,delete
명령으로 변경된 페이지를더티 페이지
라고 부른다.- InnoDB는 이 더티 페이지들을
모았다가
주기적으로 이벤트를 발생시켜서한번에 디스크에 반영
한다. - 이렇게 변경된 데이터를
한번에 모았다가 처리하는
이유로는랜덤 I/O를 줄이기 위해서
이다.
- InnoDB는 이 더티 페이지들을
어댑티브 해시 인덱스
- 인덱스 키와 페이지의
주소값 쌍으로
구성된 인덱스 이다. - 페이지에 빠르게 접근하기 위해 사용되는데 어댑티브 해시 인덱스는 사용자가 자주 요청하는 데이터에 대해서
InnoDB가 자동
으로 만들어 준다. - 원하는 페이지에 빠르게 접근할 수 있기 때문에
쿼리를 더 빠르게 처리
할 수 있다.
MyISAM 스토리지 엔진

MyISAM 스토리지 엔진에서는 클러스터링
과 트랜잭션
그리고 외래키를 지원하지 않는다
.
- 테이블 단위로 잠금을 걸기 때문에
동시 처리에 불리하다
- MyISAM에도 InnoDB 버퍼풀과 같은 역할을 하는
키 캐시가 존재한다.
- 버퍼 풀과 달리 키 캐시는
인덱스만을 대상으로 동작
하는 것이 특징이다. - MyISAM은
전문 검색
,공간 좌표 검색
기능 등을 지원 한다.
마무리
- 결국, InnoDB 스토리지 엔진이
기본 엔진으로 채택
되었다. - 하지만, MySQL의 시스템 테이블은 여전히 MyISAM을 사용하고 있다는 것이다.
- MySQL 8.0부터는 모든 테이블이 InnoDB 스토리지 엔진을 사용하게 되었고 MyISAM에서만 지원하던
전문 검색
기능이나공간 좌표 검색
기능을InnoDB에서도 제공
하게 되었다는 것이다.