- 04 아키텍처
- MySQL 엔진 아키텍처
- MySQL 엔진?
- 클라이언트의 DB 접속, 쿼리 실행과 관련 ↔ 스토리지 엔진 - 데이터 저장, 읽기
- MySQL과 스토리지 엔진을 연결 해주는 핸들러 API. MySQL이 스토리지 엔진에게 실행을 전달 할 수 있는 접수 창구
- 스토리지 엔진으로부터 데이터를 받아와서 데이터를 가공하고 연산하는 논리 작업을 한다.
- MySQL 스레딩 구조
- 포그라운드 스레드
- 앞단에서 일을 처리하는 스레드. DB 접속 관련 스레드,
- 백그라운드 스레드 - BackEnd 처럼 뒤에서 역할을 수행
- Insert Buffer를 병합하는 스레드
- 로그를 디스크로 기록하는 스레드
- InnoDB 버퍼 풀의 데이터를 디스크에 기록하는 스레드
- 데이터를 버퍼로 읽어 오는 스레드
- 잠금이나 데드락을 모니터 하는 스레드
- 메모리 할당 및 사용구조 - 두개의 가장 큰 차이는 스레드간 공유되냐 안되냐.
- 글로벌 메모리 영역
- 로컬 메모리 영역
- 플러그인 스토리지 엔진 모델과 컴포넌트
- 스토리지 엔진을 추가해서 사용 할 수 있는 기능을 의미한다.
- 컴포넌트는 플러그인 모델의 단점을 보완 했다.
- 쿼리 실행 구조
- 쿼리 파서
- DOM 파서와 같이, 쿼리 문장 → 토큰화 → 토큰을 가지고 트리구조 만들기
- 전처리기
- 쿼리 파서에서 만들어진 토큰 트리구조를 가지고, 그 토큰을 실제 데이터와 연결하는 작업
- 옵티마이저
- 주어진 쿼리 문장을 가장 빠르게 처리하는 방법을 결정하는 역할
- 실행 엔진
- 옵티마이저에서 결정된 실행계획을 스토리지 엔진에게 요청을 보내는 역할
- 핸들러(스토리지 엔진)
- 실행 엔진으로부터 받은 요청을 실제로 수행하는 역할. 디스크에서 데이터를 기록하거나 찾아서 읽어오는 역할
- 복제
- 쿼리 캐시
- 현재는 제거된 기능.
- 요청 받은 쿼리를 캐시화해서 데이터와 함께 저장해두던 공간.
- 데이터 변경이 일어나면 캐시에 있는 쿼리와 데이터의 변경도 필요하므로 비용 증가와 버그 발생 가능성 증가
- 스레드 풀
- 사용자의 요청을 처리하기 위해 스레드를 무한정 늘린다면, 그 스레드가 CPU에서 컨텍스트 스위치에 의해 발생하는 오버헤드의 비용이 점점 늘어나 효율이 떨어진다.
- 그러므로 가장 효율적인 스레드의 갯수를 설정하여, 스레드의 갯수를 조절하여 오버헤드를 줄이고자 한다.
- 트랜잭션 지원 메타데이터
- 테이블의 구조정보와 스토어드 프로그램 등의 정보를 파일이 아닌 테이블 형태로 저장한다.
- 파일로 저장하면 트랜잭션의 도움을 받지 못하기에, 이러한 방식을 택한다.
- InnoDB 스토리지 엔진 아키텍처
- Primary Key에 의한 클러스터링
- Primary key 값의 순서대로 디스크에 저장 → 처리에 대한 기준점
- 외래 키 지원
- 부모 테이블과 자식 테이블이 엮이게 되기 때문에, 수정과 삭제 작업에 있어서 일관성을 위해 두개의 테이블을 체크하는 작업이 발생 → 이 작업은 결국 Lock을 통해 실행 → 성능 저하
- MVCC(Multi Version ConCurrent Control)
- Update와 같은 데이터 변경이 일어날 때, Undo 로그라는 공간에 원본 데이터를 넣어둔다.
- 이 넣어둔 데이터를 통해, 잠금 없이 데이터 읽기를 지원 해준다.
- READ_UNCOMMITED - 커밋 되지 않은 데이터, 즉 InnoDB 버퍼 풀에 담겨진 변경된 데이터를 반환 받겠다.
- READ_COMMITED - Undo 영역의 데이터를 반환 받겠다.
- 같은 데이터를 요구 받아도, 격리 수준에 따라 다른 버전(Multi version)으로 데이터를 반환 할 수 있다. 그리고 업데이트가 이루어 지는 과정에서도 락에 상관 없이(Concurrent) 반환 할 수 있다.
- Non-Locking Consistent Read
- MVCC 덕분에 가능한 기능이다.
- 다른 트랜잭션의 잠금을 기다리지 않고, 순수한 SELECT 읽기 작업을 바로 실행 해준다.
- 자동 데드락 감지
- 교착 상태에 빠진 트랜잭션들을 데드락 감지 스레드를 이용해 체크하고, 가장 적은 Undo 레코드를 가진 트랜잭션을 먼저 종료 시킨다.
- Undo 레코드가 적다는 것은 그만큼 변경할 내용이 적다는 의미. 실행되는 작업에서 가장 중요도가 떨어진다는 의미.
- 자동화된 장애 복구
- 급작스러운 하드웨어 문제로 인해 데이터의 문제가 발생 할 경우. 이것을 고치기 위해 자동으로 발생 한다.
- 하지만 문제가 심해지면 자동 복구하는 과정에서 MySQL서버가 계속해서 꺼지게 된다.
- 이 꺼지는 동작을 막기 위해 innodb_force_recovery를 통해 수동으로 복구 작업을 실시한다.
- InnoDB 버퍼풀 - 가장 핵심적인 부분. 1) 디스크에서 가져온 데이터를 캐싱 2) 쓰기 작업 지연을 위한 버퍼
- 버퍼 풀의 크기 설정
- innodb_buffer_pool_size를 통해 크기를 변경 할 수 있다.
- 버퍼 풀의 구조
- LRU 리스트, Flush 리스트, Free 리스트 총 3개로 구성된다.
- Free 리스트
- 디스크에서 새롭게 데이터 페이지를 담아오기 위한 공간.
- LRU (Least Recently Used)
- 디스크에서 가져온 데이터를 저장하는 메모리 공간.
- 페이지 aging 시스템을 통해 사용되지 않는 페이지를 걸러 낼 수 있다.
- Old 서브리스트 (LRU) + New 서브리스트 (MRU)
- MRU는 디스크에서 가져와서 사용된 적이 있는 페이지가 존재 한다. LRU는 아직 사용되지 않은 페이지들이 존재 한다.
- Flush 리스트(Redo 로그)
- 더티 페이지의 변경 시점 기준의 페이지 목록을 관리. → 어떠한 것들이 변경 됐는지에 대한 정보
- 변경된 데이터 페이지는 Inno 버퍼 풀에 존재한다.
- 버퍼 풀과 리두 로그
- 쓰기 지연 버퍼링을 기능을 한다.
- Redo 로그를 활성 리두 로그, 나머지 부분으로 나누고 활성 리두 로그에서 기록이 될 때마다 LSN(Log Sequence Number)를 측정하여, 버퍼링된 데이터를 동기화할 시점을 결정한다.
- 버퍼 풀에 존재하는 더티 페이지 뿐만 아니라 리두 로그에 존재하는 엔트리(정보)도 함께 동기화 된다.
- 버퍼 풀 플러시
- 버퍼링이 다 차지 않아도 디스크에 동기화 하고자 할 때 사용하는 함수
- 버퍼 풀 상태 백업 및 복구
- Mysql을 재시작하면 버퍼 풀이 비게 되므로, 캐싱의 기능을 다시 캐시가 차기전까지는 이용 할 수 없음 → 그래서 재시작 전에 존재하는 버퍼 풀의 상태를 저장하고, 재시작 한 뒤 다시 로드함.
- Dobule Write Buffer
- 실제 데이터 파일의 쓰기가 중간에 실패 할 때를 방지하기 위해 사용.
- 더티 페이지만을 보아서 또다른 메모리 공간에 저장 해둠.
- 언두(Undo) 로그
- 체인지 버퍼
- 디스크로부터 읽어와서 인덱스 업데이트와 같은 작업 비용을 후순위로 미루기 위해, 버퍼에 저장해서 사용자에게 결과 반환
- 리두(Redo) 로그 및 로그 버퍼
- 디스크에서 가져온 데이터를 대상으로 한다 ↔ [언두 로그] - 사용자가 입력한 데이터를 대상
- 어뎁티브 해쉬 인덱스(Adaptive Hash Index)
- 자주 요청된 데이터에 대해 InnoDB 스토리지 엔진이 자동으로 생성 해주는 인덱스
- B-tree 인덱스의 작업을 도와주기 위함. → B-tree보다 선행되서 이 인덱스를 통해 검색이 수행됨.
- 하지만 중요한 점은 데이터의 검색은 메모리 상에서의 데이터를 찾을 때 사용 목적이 있음. 디스크에서의 검색과는 상관 없음 → 디스크 읽기가 많은 경우는 도움 X
- 인덱스를 보관하기 위한 메모리 공간이 추가적으로 필요 → 단점
- 테이블의 내용이 삭제 또는 변경 → 어뎁티브 해쉬 인덱스도 변경이 필요 → 비용 추가
- MyISAM 스토리지 엔진 아킽텍처
- MySQL 로그 파일
- 에러 로그 파일
- log_error 파일, .err 확장자를 가짐
- 전체적으로 나중에 실제적으로 로그 파일을 확인 해야할 경우에 다시 공부. 밑에 부분 공부 안했음.
- 제너럴 쿼리 로그 파일
- 슬로우 쿼리 로그 파일