본문 바로가기

프로그래밍/데이터베이스

[Real Mysql] Mysql 아키텍처

Mysql 전체 구조

 

MySQL 엔진

 

클라이언트의 접속과 요청을 처리하는 커넥션 핸들러와 SQL 파서 및 전처리기가 있고,

쿼리를 최적화시켜주는 SQL 옵티마이저가 있다.

SQL 문장을 분석하고 최적화 시키는 중요 업무를 수행

 

스토리지 엔진

 

실제 데이터를 디스크에 저장하거나 디스크에서 데이터를 읽어오는 부분

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

테이블을 생성할때 스토리지 엔진을 정하면 해당 테이블의 읽기 작업이나 변경 작업은 해당 스토리지 엔진이 처리하게 된다.



핸들러 요청

핸들러 요청은 MySQL 엔진에서 스토리지 엔진에 보내는 요청

 

핸들러 API

핸들러 요청에서 사용되는 API

 

 

스레드 구조

MySQL 은 프로세스 기반이 아니라 스레드 기반으로 동작한다.

크게 포그라운드 스레드와 백그라운드 스레드로 구분된다.

 

포그라운드 스레드

 

먼저 캐시된 스레드는 스레드 풀에 존재하는 스레드를 뜻한다.

포그라운드 스레드는 MySQL 서버에 접속된 클라이언트의 수만큼 존재하고,

각 클라이언트가 요청하는 쿼리 문장을 처리한다.

 

사용자가 작업을 마치고 커넥션이 종료되면 해당 스레드는 스레드 캐시(스레드풀)로 돌아간다.

이때 스레드풀에 이미 thread_cache_size 만큼 있다면 스레드를 스레드 캐시에 넣지 않고 종료시켜버린다.

 

InnoDB 는 데이터 버퍼나 캐시까지만 포그라운드 스레드가 처리하고

나머지 버퍼에서 디스크까지 기록하는 작업은 백그라운드 스레드가 처리한다.

 

백그라운드 스레드

 

InnoDB는 많은 수의 백그라운드 스레드가 있는데,

insert 버퍼를 병합하는 스레드, 로그를 디스크로 기록하는 스레드, 버퍼 풀의 데이터를 디스크에 기록하는 스레드,

데이터를 버퍼로 읽어오는 스레드, 여러가지 잠금이나 데드락을 모니터링 하는 스레드가 있고

이런 모든 스레드를 총괄하는 메인 스레드도 있다.

 

가장 중요한 건 로그 스레드와 버퍼 데이터를 디스크로 내려쓰는 write 쓰레드다.

 

 

메모리 할당 및 사용구조

 

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

 

글로벌 메모리 영역

 

글로벌 메모리 영역은 MySQL 서버가 시작되면 운영체제가 무조건 할당하는 공간.

글로벌 메모리 영역은 많은 스레드가 공유할 수 있는 공간이다.

 

로컬 메모리 영역

 

로컬 메모리 영역은

클라이언트 요청을 스레드가 쿼리를 처리하는데 사용하는 메모리 영역이다.

클라이언트 영역이라고도 하고,

클라리언트와 MySQL 서버와 커넥션을 세션이라고 해서, 세션 메모리 영역이라고도 한다.

 

스레드 별로 독립적으로 할당 되고 공유되지 않는다.

커넥션이 열려있는 동안 계속 할당된 상태로 남아있는 공간도 있고(커넥션 버퍼, 결과 버퍼)

쿼리가 끝나면 해제 되는 공간(소트버퍼, 조인버처)도 있다.

 

 

 

쿼리 실행 구조

쿼리 파서

 

쿼리 문장을 MySQL 이 인식할 수 있는 최소 단위인 토큰으로 분리해서 트리 형태의 구조로 만들어낸다.

쿼리의 기본 문법 오류는 이 과정에서 발견된다.

 

전처리기

 

파서 트리를 기반으로 쿼리 문장에 구조적인 문제점이 있는지 확인한다.

테이블 이름이나, 컬럼 이름, 접근 권한 등을 확인하는 과정을 한다.

테이블이 존재하지 않거나 권한상 사용할 수 없다면 이 과정에서 발견된다.

 

옵티마이저

쿼리 문장을 저렴한 비용으로 가장 빠르게 처리할지 결정하는 역할을 한다.

이 책의 내용도 대부분 옵티마이저가 선택하는 내용을 설명하는것이고, 어떻게 하면 옵티마이저가 더 좋은 선택을 할 수 있게 할지 유도하는걸 알려주는것.

역할이 아주 중요하다.

 

쿼리 실행기

옵티마이저로부터 내려온 계획을 스토리지 엔진에 요청해서 받은 결과를 또 다른 핸들러 요청의 입력으로 연결하는 역할

 

스토리지 엔진

MySQL 쿼리 실행기의 요청에 따라 데이터를 디스크로 저장하고 디스크로부터 읽어오는 역할을 한다.

 

 

복제

2대 이상의 MySQL 서버가 동일한 데이터를 담도록 실기산으로 동기화 하는 기술

MySQL에서는 쓰기와 읽기의 역할로 구분을 해서 전자를 마스터, 후자를 슬레이브라 한다.

 

마스터

마스터 서버에서 데이터 구조나 내용을 변경하는 쿼리 문장은 바이너리 로그에 기록한다.

슬레이브 에서 변경 내역을 요청하면 마스터는 바이너리로그를 읽어서 슬레이브에 넘긴다.

Binlog dump 스레드가 이 일을 전담하는 스레드가

 

슬레이브

슬레이브에서는 릴레이 로그를 가지고 있다.

슬레이브의 I/O 스레드는 마스터 서버에 접속해 변경 내역을 요청하고 변경 내역을 릴레이 로그에 저장한다.

슬레이브 서버의 SQL 스레드가 릴레이 로그에 기록된 변경 내역을 재실행해서 마스터와 동일한 상태를 유지하게 한다.

 

 

쿼리 캐시

 

 

실행 결과를 캐시에 담아두고, 동일한 쿼리 요청이 왔을 때 간단하게 쿼리 캐시에서 찾아서 바로 줄 수 있다.

 

쿼리의 결과를 메모리에 캐시해 두는 것

 

쿼리 캐시의 결과를 내려주기 전에 반드시 다음 확인 절차를 거쳐야 한다.

 

- 요청 쿼리 문장이 쿼리 캐시에 존재하는지?

- 해당 사용자가 결과를 볼 권한을 가지고 있는지?

- 트랜잭션 내에서 실행된 쿼리인 경우 가시 범위 내에 있는지

가시범위란? 트랜잭션은 자신의 ID 보다 ID 값이 큰 트랜잭션에서 변경한 작업 내역이나 쿼리 결과는 참조 할 수 없다.

- CURRENT_DATE 등 호출 시점에 따라 결과가 달리지는게 있는지?

- Prepare Statement 의 경우 변수가 결과에 영향을 미치지 않는지?

- 캐시 한 후 해당 데이터가 다른 사용자에 의해 변경되진 않았는지?

 

 

InnoDB

InnoDB 는 Mysql 스토리지 엔진중 거의 유일하게 레코드 기반 잠금을 제공해서 높은 동시성처리가 가능하고 안정적이며 뛰어나다.

 

 

InnoDB 스토리지 엔진의 특성

- PK key에 의한 클러스터링 : InnoDB는 기본적으로 모든테이블이 PK를 기준으로 클러스터링 되어 저장된다.

PK 값 순서대로 디스크에 저장되고, 따라서 PK 레인지 스캔이 상당히 빨리 처리된다.

 

- 잠금이 필요 없는 일관된 읽기: MVCC 란 기술을 이용해 락을 걸지 않고 읽기 작업을 수행한다.

 

- 외래키 지원: 엔진 레벨에서 외래키를 지원하는데 외래키는 부모 테이블이나 자식 테이블에 데이터가 있는지 체크하는 작업이 필요해서 잠금이 여러 테이블로 전파되고 데드락이 발생 할 수 있다. 

 

- 자동 데드락 감지: 발생함과 동시에 감지하고 rollback 이 가장 용이한 트랜잭션부터 종료시킨다.

 

 

InnoDB 버퍼풀

- 디스크의 데이터 파일이나 인덱스 정보를 메모리에 캐시해 두는 공간.

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

버퍼 풀이 데이터를 모아서 처리하게 되면 디스크 작업의 횟수를 줄일 수 있다.

 

- 버퍼 풀이 디스크에 아직 기록되지 않은 변경된 데이터를 가지고 있는데 이걸 더티페이지라고 부른다.

 

언두 로그

언두 영역은 Update 나 Delete 같이 데이터를 변경할때 변경되기 전의 데이터를 보관하는곳.

트랜잭션의 롤백을 대비하는 공간이다.

 

인서트 버퍼

레코드가 insert 나 update 될때는 데이터 파일 변경 뿐만아니라 테이블에 포함된 인덱스를 업데이트 하는 작업도 필요하다.

 

근데 인덱스 업데이트 작업은 디스크를 읽는 작업이 필요할때가 있어서 테이블에 인덱스가 많으면 

상당히 많은 자원 소모가 된다. 그래서 InnoDB는 변경 할 인덱스 페이지가 버퍼 풀에 있으면

업데이트를 바로 하지만, 그렇지 않고 디스크에서 읽어와야 한다면 즉시 실행하지 않고 

임시공간에 저장해 성능을 향상 시킨다.

 

이떄 사용되는 임시 메모리 공간을 인서트 버퍼라고 한다.

 

인서트 버퍼에 임시로 저장돼있는 레코드 조각은 이후 백그라운드 스레드에 의해 병합 되는데

이 역할을 하는 스레드를 인서트 버퍼 머지 스레드 라고 한다.