mysql 아키텍쳐[1] - 아키텍쳐 개요
* 이글은 RealMysql 책을 참고하여 작성한 내용입니다.
db 성능은 디스크 접근횟수에 달려있다.
일반적으로 메모리는 디스크보다 100~1만 배 빠르다.
최대한 메모리에서 많은 작업을 하고 디스크 접근을 최소화해야한다.
또한 메모리 데이터를 디스크에 동기화할 땐 안전하게 처리할 수 있어야한다.
mysql db를 공부하며 위의 전제조건이 중요하다.
mysql 아키텍쳐 설계에 기본이 되는 내용이다.
위 내용을 중점에 두고 글을 이해해보자.
mysql 아키텍쳐
[mysql 엔진]
- 커넥션 핸들러
- 클라이언트 접속 허용
- 쿼리 실행 요청 - sql 파서
- 쿼리를 토큰(mysql이 인식할 수 있는 최소 단위의 어휘나 기호)으로 분리해 트리 형태의 구조를 만들어 내는 작업
- 기본 sql 문법 오류 검사 - sql 전처리기
- 쿼리 파서에 의해 만들어진 트리를 기반으로 테이블, 칼럼, 내장 함수 등이 실제 존재하는지 검증
- 접근 권한 검증 - 옵티마이저
- 쿼리 변환
- 비용 최적화 실행 계획 수립 - 캐시 & 버퍼
- 캐시 : SELECT 쿼리의 결과를 저장하여 이후 요청시 해당 결과를 반환(8.0 부터 제거)
- 버퍼 : 디스크에 존재하는 데이터 파일을 메모리 공간에 두어 읽기와 쓰기 작업의 안정성과 효율을 위한 공간
- 위 그림에서 버퍼 공간이 mysql 엔진 영역으로 포함되어있지만 innoDB의 버퍼도 포함되어있다.
[스토리지 엔진]
실제 데이터를 디스크 스토리지에 저장하거나 읽어오는 역할을 담당한다.
mysql 엔진은 명령을 통해 수행되는데 핸들러 api 이루어진다.
스토리지 엔진은 여러 플러그인으로 이루어진 아키텍쳐를 갖추고 있다.
플러그인을 통해 mysql 함수, 전문 검색, 사용자 인증과 같은 기능을 제공해준다.
- innoDB (5.5 이후 기본 설정 엔진)
- 트랜잭션 지원, 외래키 지원
- mvcc 지원(락 미사용으로 동시 처리 성능 효율)
- 클러스터 인덱스 사용 - MyIsam
- 트랜잭션 미지원(빠른 읽기 성능)
- 테이블 단위의 락 동작 방식(동시 처리 성능 비효율)
mysql 스레딩 구조
mysql 스레드는 mysql 엔진에서 주로 활동하는 스레드와 스토리지 엔진에서 주로 활동하는 스레드가 존재한다.
- 포그라운드 스레드
- 클라이언트 스레드 : 커넥션 연결시 사용자에게 할당되는 스레드
mysql 엔진 영역에서만 활동(디스크 접근 X) - 백그라운드 스레드
- 로그 스레드 : 로그를 디스크에 기록
- 버퍼 스레드 : 버퍼 풀의 데이터를 디스크에 저장
- 기타 등등 디스크의 데이터 파일을 메모리로 가져오거나 모니터링을 수행하는 수많은 백그라운드 스레드가 존재함
사용자의 요청 처리를 빠르게 진행하기 위해 자주 사용되는 데이터는 메모리 공간에 적재되어있고 포그라운드 스레드는 메모리 공간에 주로 작업을 수행한다.
백그라운드 스레드는 메모리 데이터와 디스크 데이터를 동기화하는데 주로 사용된다.
만일, 메모리 공간에 적재된 데이터가 없다면 백그라운드 스레드를 통해 디스크에 접근해서 데이터를 가져오는데 이러한 처리는 성능 저하의 원인이 된다.
mysql의 내부 알고리즘에 의해 이러한 접근이 자주 발생하지는 않지만 서버 재시작을 하는 경우 메모리가 비워져 있을 수 있다. 허나, 최신 mysql에서는 메모리 공간의 데이터 페이지도 백업하여 재시작시 적재하도록 하여 성능저하를 막는다.
버퍼풀
버퍼풀의 역할
- 디스크의 데이터 파일이나 인덱스 정보를 저장함 (읽기 성능 향상 목적)
- 변경 사항이 발생한 데이터의 정보를 저장하고 한번에 디스크에 저장하는 버퍼 역할 (쓰기 성능 향상 목적)
버퍼의 구조
- innoDB 버퍼풀
- **언두 로그** : 트랜잭션 시작 전 여러 버전의 데이터
- 데이터 페이지
- 메모리에 존재하는 데이터를 페이지 형태로 저장
- 클린 페이지 : 변경사항이 없음
- 더티 페이지 : 변경사항이 있음
- **로그 버퍼**
- 리두 로그 : 트랜잭션 커밋 후 리두 로그가 기록되고 즉시 디스크의 리두 로그 파일에 기록됨
- 시스템 테이블 스페이스(디스크 영역)
- 데이터 파일, 인덱스 정보 등 디스크에 영구 저장되는 정보 - 리두 로그 (디스크 영역)
- 로그 버퍼의 리두 로그가 디스크에 영구 저장되는 정보
InnoDB와 MyIsam의 차이
1. pk에 의한 클러스터링
InnoDB의 모든 테이블은 기본적으로 pk를 기준으로 클러스터링되어 저장된다. 즉, pk 순서대로 저장된다.
모든 세컨더리 인덱스는 레코드 주소가 아닌 pk의 값을 논리적인 주소로 사용한다.
따라서 pk를 통한 조회가 세컨더리 인덱스를 통한 조회보다 항상 빠르다.
2. 외래키 지원
외래키는 부모-자식 테이블간의 관계를 설정해주는 장점이 있지만, 단점 또한 존재한다.
변경시 부모 테이블과 자식 테이블 모두 데이터가 존재하는지 체크를 해야하므로 잠금이 전파되고, 인덱스 또한 부모-자식 테이블 모두에 필요로 한다.
3. MVCC(Multi Version Concurrency Control)
잠금을 사용하지 않더라도 일관된 읽기를 가능하도록 하는 기능을 제공해주는 것이 MVCC이다.
InnoDB의 메모리 영역에는 버퍼풀과 로그 영역이 존재하고 디스크에 데이터 파일이 존재한다.
만약, 트랜잭션을 시작하고 특정 레코드를 update하면 버퍼풀과 디스크 영역의 레코드는 update 된다.
허나 로그 영역의 언두 로그는 변경되기 이전에 데이터를 보관하고 있다.
트랜잭션이 커밋되기 전에 다른 사용자가 해당 레코드를 조회하면 버퍼나 디스크가 아닌 언두 로그의 데이터를 조회하여 커밋(영구 반영)되기 이전의 데이터를 읽을수 있다.
4. 자동 데드락 감지
잠금으로 인한 교착상태에 빠지지 않기 위해 데드락 감지 스레드를 통해 주기적으로 교착상태에 빠진 트랜잭션을 찾아 강제 종료한다.
언두 레코드를 적게 가진 트랜잭션이 우선순위를 갖는다. 언두 레코드 양이 적다는 것은 롤백시 제거해야할 언두 레코드가 적다는 것이므로 롤백으로 인한 부하를 줄일 수 있다.
데드락 감지 스레드는 잠금목록을 검사하기 위해 잠금 목록을 가진 테이블을 새롭게 잠금하는데 이로인해 잠금목록에 존재하는 레코드를 변경하는 트랜잭션이 작업을 수행하지 못하여 성능 저하를 유발할 수 있다. 만약 동시 처리 스레드가 많거나, 트랜잭션에 의한 잠금 레코드가 많다면 이로인한 부하는 더욱 더 커지게 된다.
-> mysql은 데드락 감지 스레드로 인한 새로운 부하를 해결하기 위해 특정 시간이 지나면 데드락 상황이 발생한 트랜잭션을 자동으로 실패하게 할 수 있다.(innodb_lock_wait_timeout)
참고. 구글에서 자동 데드락 감지 스레드로 성능저하를 발견하게 되어 innodb_lock_wait_timeout 기능이 추가되었다고 한다.
실무에서 발견하기 어려운 결함으로 보인다.