표준화된 DBMS 구조란 존재하지 않는다. 모든 데이터베이스 구조는 다르고 내부 컴포넌트의 경계를 명확히 나누고 정의하기 어렵다. 프로젝트 문서 등에 기재된 설명과 달리 성능 최적화와 에지 케이스 처리 또는 설계적 이유로 코드 레벨에서 여러 컴포넌트를 결합하기도 한다.
공통된 DBMS 구조
여러 DBMS 구조에 대한 설명을 보면 컴포넌트와 컴포넌트간의 관계에 대한 정의가 다르다. 아래 그림은 공통된 정의를 나타낸다.
DBMS는 클라이언트/서버 모델을 기반으로 한다. 데이터베이스 인스턴스(노드)는 서버 역할을 하고, 어플리케이션 인스턴스는 클라이언트 역할을 한다.
쿼리 프로세서
클라이언트의 요청은 트랜스포트 서브시스템을 통해 전달된다. 요청은 쿼리 형태이고, 주로 특정 쿼리 언어로 표현한다. 트랜스포트 서브시스템은 쿼리를 쿼리 프로세서에 전달한다. 쿼리 프로세서는 쿼리를 해석, 분석 및 검증한다. 쿼리의 의미를 파악한 뒤에 액세스 제어를 진행할 수 있다.
분석된 쿼리는 쿼리 옵티마이저에 전달된다. 쿼리에서 논리적으로 불가능한 부분과 중복을 제거한 뒤에 내부 데이터 통계와 데이터 위치 등을 기반으로 가장 효율적인 쿼리 실행 계획을 생성한다. 옵티마이저는 쿼리 수행에 필요한 관계형 연산을 종속 트리(dependency tree) 1로 변환하는 일 외에도 인덱스 순서 선택과 카디널리티 2 예측, 액세스 메소드 선택 등의 최적화 단계를 처리한다.
쿼리는 일반적으로 실행 계획 형태로 표현한다. 실행계획은 쿼리가 요구하는 결과를 도출하는 데 수행해야 하는 일련의 작업이다. 같은 쿼리라도 여러 다른 실행 계획이 존재할 수 있으며 옵티마이저는 이 가운데 가장 효율적인 실행 계획을 선택한다.
실행 엔진
선택된 실행 계획은 로컬 및 원격 실행의 결과를 결합하는 실행 엔진이 수행한다. 원격 실행은 클러스터 3 내 여러 노드 사이의 데이터 읽기, 쓰기 및 복제를 포함한다.
스토리지 엔진
클라이언트 또는 다른 노드가 요청한 로컬 쿼리는 스토리지 엔진이 수행한다. 스토리지 엔진은 다음과 같이 명확한 역할을 담당하는 컴포넌트로 구성된다.
- 트랜잭션 매니저(transaction manager): 트랜잭션 4을 스케줄링하고 데이터베이스 상태의 논리적 일관성을 보장한다.
- 잠금 매니저(lock manager): 트랜잭션에서 접근하는 데이터베이스 객체에 대한 잠금을 제어한다. 동시 수행 작업이 물리적 데이터 무결성을 침해하지 않도록 제어한다.
- 액세스 메서드(access method): 디스크에 저장된 데이터에 대한 접근 및 저장 방식을 정의한다. 힙 파일과 B-트리 또는 LSM 트리 등의 자료구조를 사용한다.
- 버퍼 매니저(buffer manager): 데이터 페이지를 메모리에 캐시한다.
- 복구 매니저(recovery manager): 로그를 유지 관리하고 장애 발생 시 시스템을 복구한다.
트랜잭션과 잠금 매니저는 동시성을 제어한다. 논리적 및 물리적 데이터 무결성을 보장하고 동시 수행 작업의 효율적인 수행을 담당한다.
사례: MySQL 구조
파서
파서(Parser)는MySOL 엔진에 포함되는 오브젝트로, 사용자가 요청한 SQL문을 쪼개 최소단위로 분리하고 트리를 만든다. 트리를 만들면서 문법검사를 수행한다.
전처리기
전처리기(preprocessor)는 MysQL엔진에 해당하는 오브젝트로, 파서에서 생성한 트리를 토대로 SQL문에 구조적인 문제가 없는지 파악한다. SQL문에 작성된 테이블, 열, 함수, 뷰와같은 오브젝트가 실질적으로 이미 생성된 오브젝트인지, 접근권한은 부여되어 있는지 확인하는 역할을 한다.
옵티마이저
옵티마이저(optimizer)는 MySQL 의 핵심 엔진 중 하나로, DBMS에서 두뇌라고 불러도 과언이 아닐 만큼 핵심적인 역할을 수행한다. 전달된 파서 트리를 토대로 필요하지 않은 조건은 제거하거나 연산 과정을 단순화한다. 나아가 어떤 순서로 테이블에 접근할지, 인덱스를 사용할지, 사용한다면 어떤 인덱스를 사용할지, 정렬할 때 인덱스를 사용할지 아니면 임시 테이블(temporary table)을 사용할지와 같은 실행 계획을 수립한다.
단, 실행 계획으로 도출할 수 있는 경우의 수가 지나치게 많을 때는 실행 계획을 수립하고 비용을 산정하여 최적의 실행 계획을 선택하기까지 시간이 오래 걸리는 만큼 모든 실행 계획을 판단하지는 않는다. 이는 옵티마이저가 선택한 최적의 실행 계획이 최상의 실행 계획이 아닐 가능성도 있다는 걸 의미한다.
실행 계획을 수립하는 작업 자체만으로도 사용자의 대기 시간과 하드웨어 리소스를 점유하므로, 시간과 리소스에 제한을 두고 실행 계획을 선정해야 한다.
엔진 실행기
엔진 실행기(engine executor)는 MySQL 엔진과 스토리지 엔진 영역 모두에 걸치는 오브젝트로, 옵티마이저에서 수립한 실행 계획을 참고하여 스토리지 엔진에서 데이터를 가져온다. 이후 MySQL 엔진에서는 읽어온 데이터를 정렬하거나 조인하고, 불필요한 데이터는 필터링 처리하는 추가 작업을 한다. 따라서 MySQL 엔진의 부하를 줄이려면 스토리지 엔진에서 가져오는 데이터양을 줄이는 게 매우 중요하다.
출처
- Database Internals - 알렉스 페트로프
- 업무에 바로 쓰는 SQL 튜닝
Footnotes
종속 트리(dependency tree)는 데이터베이스 쿼리 최적화에서 사용되는 개념으로, 쿼리의 논리적 연산이 어떻게 수행되어야 하는지의 관계를 트리 구조로 표현한 것 ↩
카디널리티(cardinality)는 데이터베이스 쿼리에서 연산이나 데이터 집합에 포함된 고유한 값의 개수 또는 특정 테이블의 행(row)의 수를 의미 ↩
클러스터(cluster)는 여러 개의 서버나 노드가 하나의 시스템처럼 동작하는 구조 ↩
트랜잭션(transaction)은 데이터베이스에서 하나의 논리적 작업 단위로, 데이터를 처리하는 일련의 작업을 묶어서 처리하는 개념 ↩