MySQL 엔진은 클라이언트로부터의 접속 및 쿼리요청을 처리하는 커넥션 핸들러와 SQL파서 및 전처리기 그리고 쿼리의 최적화된 실행을 위한 옵티마이저가 중심을 이룬다.
성능향상을 위해 MylSAM의 키캐시나 InnoDB의 버퍼풀같은 보조 저장소 기능이 포함되 있다.
스토리지 엔진
MySQL 엔진은 요청된 SQL문장을 분석하거나 최적화하는 등 DBMS의 두뇌에 해당하는 처리를 수행하고 실제 데이터를 디스크 스토리지에 저장하거나 디스크 스토리지로부터 데이터를 읽어오는 부분은 스토리지 엔진이 전담한다. MySQL 서버에서 MySQL 엔진은 하나지만 스토리지 엔진은 여러 개를 동시에 사용할 수 있다.
MySQL 서버는 프로세스 기반이 아니라 스레드 기반으로 작동하며, 크게 포그라운드 스레드와 백그라운드 스레드로 구분할 수 있다.
포그라운드 스레드(클라이언트 스레드)
포그라운드 스레드는 최소한 MySQL 서버에 접속된 클라이언트의 수만큼 존재하며, 주로 각 클라이언트 사용자가 요청하는 쿼리 문장을 처리하는 것이 임무다. 클라이언트 사용자가 작업을 마치고 커넥션을 종료하면, 해당 커넥션을 담당하던 스레드는 다시 Thread pool로 되돌아간다. 이때 이미 스레드 풀에 일정 개수 이상의 대기중인 스레드가 있으면 스레드 풀에 넣지 않고 스레드를 종료시켜 일정 개수의 스레드만 스레드 풀에 존재하게 된다. 이렇게 스레드의 개수를 일정하게 유지하게 만들어주는 파라미터가 thread_cache_size다.
포그라운드 스레드는 데이터를 MySQL의 데이터 버퍼나 캐시로부터 가져오며, 버퍼나 캐시에 없는 경우에는 직접 디스크의 데이터나 인덱스 파일로부터 데이터를 읽어와서 작업을 처리한다. MylSAM테이블은 디스크 쓰기 작업까지 포그라운드 스레드가 처리하지만 InnoDB테이블은 데이터 버퍼나 캐시까지만 포그라운드 스레드가 처리하고 나머지 버퍼로부터 디스크까지 기록하는 작업은 백그라운드 스레드가 처리한다.
백그라운드 스레드
MylSAM의 경우에는 별로 해당 사항이 없지만 InnoDB는 여러 가지 작업이 백그라운드로 처리된다.
인서트 버퍼를 병합하는 스레드
로그를 디스크로 기록하는 스레드
InnoDB버퍼 풀의 데이터를 디스크에 기록하는 스레드
데이터를 버퍼로 읽어들이는 스레드
잠금이나 데드락을 모니터링하는 스레드
이러한 모든 스레드를 총괄하는 메인스레드
가장 중요한 것은 로그 스레드와 버퍼의 데이터를 디스크로 내려쓰는 작업을 하는 쓰기 스레드이다.
쓰기 스레드는 innodb_write_io_threads
읽기 스레드는 innodb_read_io_threads 를 통해 지정해줄 수 있다.
데이터를 읽는 작업은 주로 클라이언트 스레드에서 처리되기 때문에 읽기 스레드는 많이 설정할 필요가 없지만 쓰기 스레드는 아주 많은 작업을 백그라운드로 처리하기 때문에 일반적인 내장 디스크를 사용할 때는 2~4정도 설정한다.
InnoDB 스토리지 엔진 아키텍처
InnoDB는 MySQL에서 사용할 수 있는 스토리지 엔진 중에서 거의 유일하게 레코드 기반의 잠금을 제공하고 있으며, 때문에 높은 동시성 처리가 가능하고 안정적이며 성능이 뛰어나다.
InnoDB 스토리지 엔진의 특성
프라이머리 키에 의한 클러스터링
InnoDB의 모든 테이블은 기본적으로 프라이머리 키를 기준으로 클러스터링되어 저장된다. 즉, 프라이머리 키 값의 순서대로 디스크에 저장된다는 뜻이며, 이로 인해 프라이머리 키에 의한 레인지 스캔은 상당히 빨리 처리 될 수 있다. 결과적으로 쿼리의 실행 계획에서 프라이머리 키는 기본적으로 다른 보조 인덱스에 비해 비중이 높게 설정된다.
잠금이 필요없는 일관된 읽기(Non-locking consistent read)
InnoDB 스토리지 엔진은 MVCC(Multi Version Concurrency Control)라는 기술을 이용해 락을 걸지 않고 읽기 작업을 수행한다. 락을 걸지 않기 때문에 InnoDB에서 읽기 작업은 다른 트랜잭션이 가지고 있는 락을 기다리지 않고 읽기 작업이 가능하다(SERIALIZABLE격리 수준 제외)
자동 데드락 감지
InnoDB는 그래프 기반의 데드락 체크 방식을 사용하기 때문에 데드락이 발생함과 동시에 바로 감지되고 감지된 데드락은 관련 트랜잭션 중에서 ROLLBACK이 가장 용이한 트랜잭션을 자동적으로 강제 종료해 버린다.
InnoDB 버퍼 풀
InnoDB 스토리지 엔진에서 가장 핵심적인 부분으로, 디스크의 데이터 파일이나 인덱스 정보를 메모리에 캐시해 두는 공간이다. 쓰기 작업을 지연시켜 일괄 작업으로 처리할 수 있게 해주는 버퍼 역할도 같이한다. 일반적인 애플리케이션에서는 INSERT,UPDATE,DELETE와 같이 데이터를 변경하는 쿼리는 데이터 파일의 이곳저곳에 위치한 레코드를 변경하기 때문에 랜덤한 디스크 작업을 발생시킨다. 하지만 버퍼 풀이 이러한 변경된 데이터를 모아서 처리하게 되면 랜덤한 디스크 작업의 횟수를 줄일 수 있다.
MyISAM 키 캐시가 인덱스의 캐시만을 주로 처리하는 데 비해 InnoDB의 버퍼 풀은 데이터와 인데스 모두 캐시하고 쓰기 버퍼링의 역할까지 모두 처리하고 있다. 그 밖에도 InnoDB의 버퍼 풀은 많은 백그라운드 작업의 기반이 되는 메모리 공간이다. 따라서 InnoDB의 버퍼 풀 크기를 설정하는 파라미터(innodb_buffer_pool_size)는 신중하게 결정해야 한다. 일반적으로는 50%~80%정도로 설정한다.
InnoDB 버퍼 풀은 아직 디스크에 기록되지 않은 변경된 데이터를 가지고 있다(이러한 데이터를 가지고 있는 페이지를 더티 페이지 라고 한다.) 이러한 더티 페이지는 InnoDB에서 주기적으로 또는 어떤 조건이 되면 체크포인트 이벤트가 발생하는데 이때 Write스레드가 필요한 만큼의 더티 페이지만 디스크로 기록된다. 체크포인트가 발생한다고 해서 버퍼 풀의 모든 더티 페이지를 디스크로 기록하는 것은 아니다.
Undo 로그
언두 영역은 UPDATE문장이나 DELETE와 같은 문장으로 데이터를 변경했을 때 변경되기 전의 데이터(이전 데이터)를 보관하는 곳이다.
updatemembersetname='홍길동'wheremember_id='1';
위 문장이 실행되면 트랜잭션을 커밋하지 않아도 실제 데이터 파일(데이터/인덱스 버퍼) 내용은 “홍길동”으로 변경된다.
그리고 변경되기 전의 값이 “벽계수”였다면 언두 영역에는 “벽계수”라는 값이 백업 되는 것이다. 이 상태에서 만약 사용자가 커밋하게 되면 현재 상태가 그대로 유지되고,
만약 롤백한다면 언두 영역에 백업되있던 데이터를 다시 데이터 파일(데이터/인덱스 버퍼)로 복구한다.
Undo의 데이터는 크게 두 가지 용도로 사용되는데, 첫 번째 용도가 트랜잭션의 롤백 대비용이다. 두 번째 용도는 트랜잭션의 격리 수준을 유지하면서 높은 동시성을 제공하는데 사용된다.
인서트 버퍼(Insert Buffer)
RDBMS에서 레코드가 INSERT되거나 UPDATE될 때는 데이터 파일을 변경하는 작업뿐 아니라 해당 테이블에 포함된 인덱스를 업데이트하는 작업도 필요하다. 그런데 인덱스를 업데이트하는 작업은 랜덤 하게 디스크를 읽는 작업이 필요하므로 테이블에 인덱스가 많다면 이 작업은 상당히 많은 자원을 소모하게 된다. 그래서 InnoDB는 변경해야할 인덱스 페이지가 버퍼 풀에 있으면 바로 업데이트를 수행하지만, 그렇지 않고 디스크로부터 읽어와서 업데이트해야 한다면 이를 즉시 실행하지 않고 임시공간에 저장해두고 바로 사용자에게 결과를 반환하는 형태로 성능을 향상 시키게 되는데, 이때 사용하는 임시 메모리 공간을 인서트 버퍼라고 한다.
사용자에게 결과를 전달하기 전에 반드시 중복 여부를 체크해야 하는 유니크 인덱스는 인서트 버퍼를 사용할 수 없다. 인서트 버퍼에 임시로 저장돼 있는 인덱스 레코드 조각은 이후 백그라운드 스레드에 의해 병합되는데, 이 스레드를 인서트 버퍼 머지 스레드 라고 한다.
리두(Redo)로그 및 로그 버퍼
쿼리 문장으로 데이터를 변경하고 커밋하면 DBMS는 데이터의 ACID를 보장하기 위해 즉시 변경된 데이터의 내용을 데이터 파일로 기록해야 한다. 하지만 이러한 데이터 파일의 변경 작업은 순차적으로 많은 데이터를 한꺼번에 변경하는 것이 아니고 랜덤하게 디스크에 기록해야 하기 때문에 디스크를 상당히 바쁘게 만드는 작업이다. 그래서 이러한 부하를 줄이기 위해 대부분의 DBMS에는 변경된 데이터를 버퍼링해 두기 위해 InnoDB 버퍼 풀과 같은 장치가 포함돼 있다. 하지만 이 장치만으로는 ACID를 보장할 수 없는데 이를 위해 변경된 내용을 순차적으로 디스크에 기록하는 로그 파일을 가지고 있다. 더 정확한 명칭은 리두 로그이며, 일반적으로 DBMS에서 로그라 하면 리두로그를 지칭하는 경우이다.
리두 로그덕분에 DBMS데이터는 버퍼링을 통해 한꺼번에 디스크에 변경된 내용을 처리할 수 있고 이로 인해 상당한 성능 향상을 기대할 수 있게 됐다. 하지만 변경작업이 매우 많은 DBMS 서버의 경우에는 리두 로그의 기록 작업이 큰 문제가 되는데, 이러한 부분을 보완하기 위해 최대한 ACID속성을 보장하는 수준에서 버퍼링하게 된다. 이러한 리두 로그 버퍼링에 사용되는 공간이 로그 버퍼이다.
MVCC(Multi Version Concurrency Control)
일반적으로 레코드 레벨의 트랜잭션을 지원하는 DBMS가 제공하는 기능이며, MVCC의 가장 큰 목적은 잠금을 사용하지 않는 일관된 읽기를 제공하는 데 있다. InnoDB는 언두 로그를 이용해 이 기능을 구현한다.