본문 바로가기

CS/데이터베이스

[DB] Real MySQL 8.0 - 4.1 MySQL 아키텍처

이 글은 Real MySQL 8.0 책을 읽고 정리한 것입니다.

 

Real MySQL 8.0 1권

《Real MySQL》을 정제해서 꼭 필요한 내용으로 압축하고, MySQL 8.0의 GTID와 InnoDB 클러스터 기능들과 소프트웨어 업계 트렌드를 반영한 GIS 및 전문 검색 등의 확장 기능들을 추가로 수록했다.

www.aladin.co.kr

 

 

 


 

 

 

4.1.1 MySQL의 전체 구조

 

 MySQL은 대부분의 프로그래밍 언어로부터 접근 방법을 모두 지원한다. 위 이미지에 ‘MySQL Connectors’에 명시된 것 처럼, C, JDBC, ODBC, 등등 표준 드라이버를 제공한다. MySQL 서버는 ‘MySQL 엔진’과 ‘스토리지 엔진’으로 구분할 수 있다.

 

4.1.1.1 MySQL 엔진

 MySQL 엔진은 대표적으로 아래의 요소들로 구성된다.

  • 커네션 핸들러 : 클라이언트로부터의 접속 처리
  • SQL 파서 및 전처리기 : 쿼리 요청 처리
  • 옵티마이저 : 쿼리 최적화된 실행 지원

 

4.1.1.2 스토리지 엔진

 MySQL에서 스토리지 엔진은 실제 데이터를 디스크 스토리지에 저장하거나 디스크 스토리지로부터 데이터를 읽어오는 역할을 담당한다. MySQL 서버에서 MySQL 엔진은 하나이지만, 스토리지 엔진은 여러 개를 동시에 사용할 수 있다.

mysql> create table test_table(fd1 int, fd2 int) engine=innoDB;
Query OK, 0 rows affected (0.03 sec)

mysql> show create table test_table \\G;
*************************** 1. row ***************************
       Table: test_table
Create Table: CREATE TABLE `test_table` (
  `fd1` int DEFAULT NULL,
  `fd2` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

 

4.1.1.3 핸들러 API

 

 MySQL 엔진의 쿼리 실행기에서 데이터 처리를 수행할 때, 스토리지 엔진에 요청하는데, 이러한 요청을 ‘핸들러 요청’이라고 한다. 이때 스토리지 엔진과 대화하기 위해 사용하는 API를 ‘핸들러 API’라고 한다. 아래 명령을 통해 핸들러 API에서 얼만큼의 호출이 있었는지 확인할 수 있다.

mysql> show global status like 'Handler%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Handler_commit             | 620   |
| Handler_delete             | 8     |
| Handler_discover           | 0     |
| Handler_external_lock      | 6661  |
| Handler_mrr_init           | 0     |
| Handler_prepare            | 8     |
| Handler_read_first         | 49    |
| Handler_read_key           | 1812  |
| Handler_read_last          | 0     |
| Handler_read_next          | 4128  |
| Handler_read_prev          | 0     |
| Handler_read_rnd           | 2     |
| Handler_read_rnd_next      | 2201  |
| Handler_rollback           | 0     |
| Handler_savepoint          | 0     |
| Handler_savepoint_rollback | 0     |
| Handler_update             | 343   |
| Handler_write              | 1019  |
+----------------------------+-------+

 

4.1.2 MySQL 스레딩 구조

 

 MySQL 서버는 프로세스 기반이 아닌, 스레드 기반으로 동작한다. 스레드는 크게 포그라운드(Foreground) 스레드와 백그라운드(Background) 스레드로 나눠진다. 아래 row는 threads 테이블에 있는 row 정보를 출력한 것이다. 아래 스레드는 포그라운드 스레드이며, 실제 사용자의 요청을 처리하는 스레드이다. 기존 MySQL 스레드 모델에서는 사용자의 커넥션 마다 아래와 같은 포그라운드 스레드다 1:1로 매핑된다. 즉, 커넥션의 수 만큼, 포그라운드 스레드(커넥션을 위한)가 생성된다.(스레드 풀에서는 이와 다름)

mysql> select * from performance_schema.threads order by type, thread_id\\G;

...

*************************** 38. row ***************************
            THREAD_ID: 48
                 NAME: thread/sql/one_connection
                 TYPE: FOREGROUND
       PROCESSLIST_ID: 8
     PROCESSLIST_USER: root
     PROCESSLIST_HOST: localhost
       PROCESSLIST_DB: mysql_study
  PROCESSLIST_COMMAND: Query
     PROCESSLIST_TIME: 0
    PROCESSLIST_STATE: executing
     PROCESSLIST_INFO: select * from performance_schema.threads order by type, thread_id
     PARENT_THREAD_ID: 1
                 ROLE: NULL
         INSTRUMENTED: YES
              HISTORY: YES
      CONNECTION_TYPE: Socket
         THREAD_OS_ID: 142
       RESOURCE_GROUP: USR_default
     EXECUTION_ENGINE: PRIMARY
    CONTROLLED_MEMORY: 1095544
MAX_CONTROLLED_MEMORY: 1485936
         TOTAL_MEMORY: 1733690
     MAX_TOTAL_MEMORY: 2115555
     TELEMETRY_ACTIVE: NO

 

4.1.2.1 포그라운드 스레드(클라이언트 스레드)

 

 포그라운드 스레드의 가장 기본적인 역할은 클라이언트와 커넥션을 맺고, 요청하는 쿼리를 처리하는 것이다. 포그라운드 스레드는 데이터를 MySQL의 데이터 버퍼나 캐시로부터 가져오며, 버퍼나 캐시가 없는 경우에는 직접 디스크에서 데이터를 읽어온다.

 

 스토리지 엔진 종류에 따라 역할 범위가 달라지는데, MyISAM의 경우 디스크 쓰기 작업까지 포그라운드 스레드가 처리하지만, InnoDB에서는 데이터 버퍼나 캐시 쓰기까지만 프로그라운 스레드가 처리한다.

 

 클라이언트가 커넥션을 종료하면, 포그라운드 스레드는 스레드 캐시(Thread cache)로 되돌아가거나, 종료된다. 만약 스레드 캐시에 일정 수준 이상의 스레드가 저장되있다면, 종료되는 것이다.

 

4.1.2.2 백그라운드 스레드

 InnoDB에 경우, 아래의 작업이 백그라운드 스레드에서 수행된다.

  • 인서트 버퍼(Insert Buffer)를 병합하는 스레드
  • 로그를 디스크로 기록하는 스레드(로그 스레드)
  • InnoDB 버퍼 풀의 데이터를 디스크에 기록하는 스레드(쓰기 스레드)
  • 데이터를 버퍼로 읽어 오는 스레드
  • 잠금이나 데드락을 모니터링하는 스레드

 위 기능 중, 가장 중요한 것은 ‘로그 스레드’와 ‘쓰기 스레드’이다. InnoDB에서는 데이터를 읽는 작업을 주로 클라이언트 스레드에서 처리되기 때문에 읽기 스레드의 개수를 많이 설정하지는 않지만, 쓰기 스레드는 백그라운드에서 많은 작업을 처리하기 때문에 적당히 많은 수의 쓰기 스레드 개수를 설정한다.

 

 일반적으로 상용 DBMS에서 읽기 요청은 지연되서는 안되지만, 쓰기 작업은 버퍼링에서 일괄 처리된다. InnoDB는 이러한 방식을 채택하지만, MyISAM은 포그라운드 스레드에서 쓰기 작업까지 함께 처리하도록 되있다. 이 때문에, InnoDB에서는 Insert, Update, Delete 쿼리를 요청한 이후, 실제 디스크에 데이터가 변경될 때까지 사용자가 기다릴 필요가 없지만, MyISAM에서는 쓰기 버퍼링 기능을 지원하지 않는다.

 

→ 그렇다면, InnoDB가 MyISAM에 비해서 Insert 속도가 빠를 것이라고 이해하면 될까?
 반드시 그런 것은 아니지만, 대부분 그렇다. 버퍼 풀의 원인이 아닌 Lock과 관련한 차이 떄문이다. InnoDB는 레코드락을 지원하지만, MyISAM은 테이블락을 지원하기 때문에, 락의 관점에서 InnoDB가 더 빠른 성능을 가질 수 있다. 그러나, 퍼포먼스를 측정할 때 단순히 엔진이 아니라, 다른 사항들을 함께 확인해야 한다. 아래 내용을 참고하자.
 

Which is faster, InnoDB or MyISAM?

How can MyISAM be "faster" than InnoDB if MyISAM needs to do disk reads for the data? InnoDB uses the buffer pool for indexes and data, and MyISAM just for the index?

dba.stackexchange.com

 

 

 

4.1.3 메모리 할당 및 사용 구조

 

 MySQL에서 사용되는 메모리 공간은 크게 글로벌 메모리 영역로컬 메모리 영역으로 구분된다. 글로벌 메모리 영역은 서버 내 여러 스레드가 공유하는 메모리 영역이고, 로컬 메모리 영역은 하나의 커넥션이 사용하는 메모리 영역이다.

 

 글로벌 메모리 영역은 MySQL 서버 시작 시 OS에서 할당해주는데, OS마다 메모리 할당 정책이 달라, 실제 메모리의 양을 측정하는 것은 쉽지 않다. 따라서, 단순하게 MySQL 시스템 변수로 설정된 메모리 양만큼 할당 받는 다고 생각해도 된다.

 

 

4.1.3.1 글로벌 메모리 영역

 일반적으로 글로벌 메모리 영역은 하나만 할당되지만, 여러 개를 할당할 수도 있다. 핵심은 모든 스레드가 글로벌 메모리 영역을 함께 공유한다는 것이다. 대표적인 글로벌 메모리 영역은 아래와 같다.

  • 테이블 캐시
  • InnoDB 버퍼 풀
  • InnoDB 어댑티브 해시 인덱스
  • InnoDB 리두 로그 버퍼

 

4.1.3.2 로컬 메모리 영역

 ‘로컬 메모리 영역’ 또는 ‘세션 메모리 영역’ 또는 ‘클라이언트 메모리 영역’이라고 불린다. 클라이언트 커넥션으로부터의 요청을 처리하기 위해 스레드를 하나씩 할당하며, 그 스레드가 사용하는 영역이 로컬 메모리 영역이다.

 

 로컬 메모리 영역은 각 클라이언트 스레드별로 독립적으로 할당되며 절대 공유되어 사용되지 않는다는 특징이 있다. 또 한가지 중요한 특징은 각 쿼리의 용도별로 필요할 때만 공유이 할당되고, 필요하지 않은 경우에는 메모리 공간이 할당 조차 되지 않을 수 있다는 점이다.

 

 계속 할당되는 메모리 공간은 ‘커넥션 버퍼’ & ‘결과 버퍼’ 이며, 그렇지 않는 공간은 ‘소트 버퍼’ & ‘조인 버퍼’이다.

 

 

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

 MySQL의 독특한 구조 중 대표적인 것은 ‘플러그인 모델’이라는 것이다. 플러그인해서 사용할 수 있는 영역은 스토리지 엔진 뿐만이 아니며, 다른 영역(ex. 검색어 파서, 사용자 인증)도 플러그인해서 사용할 수 있다.

 

 MySQL에서 쿼리가 실행되는 과정은 아래 그림과 같다. 거의 대부분의 작업은 MySQL 엔진에서 수행되고, 마지막 ‘데이터 읽기/쓰기’ 작업만 스토리지 엔진에 의해 처리된다.

 

 MySQL 서버에서 MySQL 엔진은 사람 역할을 하고 각 스토리지 엔진은 자동차 역할을 하는데, MySQL 엔진이 스토리지 엔진을 조정하기 위해 핸들러라는 것을 사용하게 된다. MySQL 엔진은 각 스토리지 엔진에게 데이터를 읽어오거나 저장하도록 명령하려면

 

 MySQL 엔진이 GROUP BY 나 ORDER BY 와 같은 복잡한 처리를 수행하며, 쿼리 내용 처리를 담당하기 때문에, 스토리지 엔진이 다르더라도 쿼리 처리 내용은 대부분 동일하다. 그렇다면 어떤 스토리지 엔진을 사용하는가는 중요하지 않는 것일까??

 

 그렇지 않다. ‘데이터 읽기/쓰기’작업 처리방식이 많~이 달라질 수 있기 때문에 어떤 스토리지 엔진을 사용하는 가는 중요하다. 이 챕터에서의 반드시 알고가야되는 포인트는, "하나의 쿼리 작업은 여러 하위 작업으로 나뉘는데, 각 하위 작업이 MySQL 엔진 영역에서 처리되는지 아니면 스토리지 엔진 영역에서 처리되는지 구분할 줄 알아야 한다”라는 것이다.

 

 

4.1.5 컴포넌트

 MySQL 8.0부터는 기존의 플러그인 아키텍처가 몇몇 단점을 갖고 있었기 때문에, 이를 대체하기 위해 컴포넌트 아키텍처가 지원된다. 플러그인 아키텍처의 단점은 아래와 같다.

  • 플러그인은 오직 MySQL 서버와 인터페이스할 수 있고, 플러그인끼리는 통신할 수 없다.
  • 플러그인은 MySQL 서버의 변수나 함수를 호출하기 때문에 안전하지 않다.
  • 플러그인은 상호 의존 관계를 설정할 수 없어서 초기화가 어렵다.

 

4.1.6 쿼리 실행 구조

 위 그림은 쿼리 실행 과정을 나타낸 것이다.

 

4.1.6.1 쿼리 파서

 쿼리 파서는 사용자 요청으로 들어온 쿼리 문장을 토큰(MySQL이 이해할 수 있는 최소 단위의 어휘나 기호)으로 분리해 “트리 형태의 구조”로 만들어 내는 작업을 의미한다. 쿼리에 대한 기본 문법 오류는 이때 확인되고, 사용자에게 오류 메시지가 전달된다.

 

4.1.6.2 전처리기

 전처리기 에서는, 쿼리 파서를 통해 도출된 “파서 트리”를 기반으로, 쿼리 문장에 구조적인 문제가 있는지 확인한다. 파서 트리에 있는 토큰들을 테이블 이름이나 컬럼 이름, 함수 와 같은 MySQL의 실제 객체에 매핑시켜, 실제 객체가 존재하는 지와 객체에 대한 접근 권한을 확인한다. 만약 실제 존재하지 않거나 권한이 없다면, 이 과정에서 발견되어 처리된다.

 

4.1.6.3 옵티마이저

 옵티마이저란 사용자의 요청으로 들어온 쿼리 문장을, 어떻게 저렴한 비용으로 가장 빠르게 처리할지를 결정하는 역할을 수행하며, DBMS의 두뇌에 해당한다.

 

4.1.6.4 실행 엔진(쿼리 실행기)

 

 회사에 비유하자면, 옵티마이저는 회사의 경영진, 실행 엔진은 중간 관리자, 핸들러는 실무자로 이해할 수 있다. 실행 엔진은 옵티마이저가 만든 계획대로, 각 핸들러에게 요청해서 받은 결과를 다른 핸들러 요청의 입력으로 연결하는 역할을 수행한다.

 

4.1.6.5 핸들러(스토리지 엔진)

 핸들러는 MySQL 서버의 가장 밑단에서 MySQL 실행 엔진의 요청에 따라 데이터를 디스크로 저장하고 디스크로부터 읽어 오는 역할을 담당한다.

 

 

4.1.8 쿼리 캐시

 (과거에) MySQL 서버에서 쿼리 캐시는 빠른 응답을 필요로 하는 웹 기반의 응용 프로그램에서 매우 중요한 역할을 담당하였다. 그러나, 몇몇 이슈로 인해, 동시 처리 성능 저하와 많은 버그의 원인이 되었다.

 

 MySQL 8.0으로 올라오면서 쿼리 캐시는 없어졌다. 실제로 쿼리 캐시 기능이 큰 도움이 되었던 서비스는 거의 없었으며, 이러한 이유로 MySQL 서버에서 쿼리 캐시를 제거한 것은 좋은 선택이다.

 

 

4.1.9 스레드 풀

MySQL 서버 엔터프라이즈 에디션에서는 스레드 풀을 제공하지만, 커뮤니티 에디션은 스레드 풀을 제공하지 않는다. MySQL 커뮤니티 에디션에서 스레드 풀을 사용하려면, Percona Server에서 제공하는 스레드풀 플러그인을 MySQL 커뮤니티 에디션에 설치해야 한다.

 

 스레드 풀의 목적은 서버의 자원 소모를 줄이는 것이다. 내부적으로 사용자의 요쳥을 처리하는 스레드 개수를 줄여서, 동시 처리되는 요청이 많다 하더라도 CPU가 제한된 개수의 스레드 처리에만 집중하도록 하는 것이다. 실제 서비스에서 눈에 띄는 향상을 보여준 경우는 드물었으며, CPU 스케줄링 과정에서 CPU 시간을 제대로 확보하지 못하는 경우에는 오히려 쿼리 처리가 더 느려지는 사례도 발생할 수 있음으로 주의해야한다.

 

 다만, 제한된 스레드만으로 CPU가 처리하도록 유도한다면, CPU의 프로세서 친화도도 높이고, OS의 불필요한 컨텍스트 스위칭 비용을 줄일 수 있다. Percona Server는 기본적으로 CPU의 코어 수만큼 스레드 풀의 크기를 설정한다. 시스템 변수를 변경하여 스레드 풀의 크기를 조정할 수 있지만, 일반적으로 코어의 개수와 스레드 풀의 크기를 맞추는 것이, CPU 프로세서 친화도를 높이는 데 좋다.

 

 그런데 만약 스레드 풀에 요청을 전달했는데, 스레드 풀의 스레드들이 모두 처리중인 상태라면, 추가적인 작업 스레드를 생성할지 또는 기존 작업 스레드의 작업이 종료될 때까지 기다릴지 판단해야 한다. 이러한 판단을 위해 스레드 풀의 “타이머 스레드”가 사용된다.

 

 타이머 스레드는 설정한 시간만큼 지난 이후 작업 스레드가 현재 처리 중인 작업을 끝내지 못했다면, 새로운 작업 스래드를 생성하여 스레드 그룹에 추가시킨다. (이때 전체 스레드 풀의 최대 크기를 넘어설 수는 없다.) 스레드 풀의 스레드들이 모두 작업 처리중일 때, 스레드 요청이 들어오면, 해당 스레드는 반드시 설정한 시간만큼은 대기해야한다. 응답 시간이 민감한 서비스라면 대기 시간을 낮춰야 되지만, 만약 0에 가까울 정도로 낮춘다면, 차라리 스레드 풀을 사용하지 않는 것이 권장된다.

 

 스레드 풀에는 선순위 큐와 후순위 큐를 통해 특정 트랜잭션이나 쿼리를 우선적으로 처리하는 기능도 제공한다. 이를 통해 락에 대한 경합 시간을 단축시켜 전체 트랜잭션의 처리 속도를 빠르게 할 수 있다.

 

4.1.10 트랜잭션 지원 메타데이터

 

SQL Stored Procedures

W3Schools offers free online tutorials, references and exercises in all the major languages of the web. Covering popular subjects like HTML, CSS, JavaScript, Python, SQL, Java, and many, many more.

www.w3schools.com

 DB에서 테이블 구조 정보와 저장 프로시저(Stored Procedures) 등의 정보를 데이터 딕셔너리 또는 메타데이터라고 한다. MySQL 서버는 5.7버전 까지, 데이터 딕셔너리를 파일 형태로 저장하였다. 그런데, 파일 기반의 메타데이터는 생성 및 변경 작업에 대해 트랜잭션을 지원하지 않기 때문에, 테이블 변경 도중 MySQL 서버가 비정상적으로 종료되면, 파일이 깨져머리는 문제가 있었다.

 

 이를 해결하기 위해, MySQL 8.0 버전 부터는, 데이터 딕셔너리를 InnoDB의 테이블에 저장하도록 개선되었다. 또한, 시스템 테이블 모두 InnoDB 스토리지 엔진을 사용하도록 개선했으며, 시스템 테이블과 데이터 딕셔너리 정보를 모두 모아서 mysql DB에 저장하고 있다.

 

 

 

 

참고자료

 

MySQL :: MySQL 8.0 Reference Manual :: 16.11 Overview of MySQL Storage Engine Architecture

16.11 Overview of MySQL Storage Engine Architecture The MySQL pluggable storage engine architecture enables a database professional to select a specialized storage engine for a particular application need while being completely shielded from the need to m

dev.mysql.com

 

'CS > 데이터베이스' 카테고리의 다른 글

[DB] SQL -DDL  (0) 2024.01.17
[DB] SQL 개요  (0) 2024.01.17
[DB] 데이터베이스 기본개념 관련 질문정리  (2) 2024.01.11
[DB] 데이터 모델링  (0) 2024.01.11
[DB] 스키마와 데이터베이스 3단계 구조  (0) 2024.01.10