MySQL 엔진 아키텍처, InnoDB 스토리지 엔진 아키텍처, MyISAM 스토리지 엔진 아키텍처, MySQL 로그 파일을 알아보자!
MySQL 엔진
: 클라이언트로부터의 접속
및 쿼리 요청
을 처리하는 커넥션 핸들러
와 SQL 파서
및 전처리기
, 쿼리 최적화 실행을 위한 옵티마이저
가 중심
=> DBMS의 두뇌
스토리지 엔진
: 실제 데이터를 디스크 스토리지에 저장
하거나 디스크 스토리지로부터 데이터를 읽어
오는 부분 전담
=> 동시에 여러개 사용 가능
create table test(col1 int, col2 int) engine=innodb;
핸들러(Handler) 요청
: MySQL 엔진의 쿼리 실행기에서 데이터를 쓰거나 읽어야 할 때 각 스토리지 엔진에 쓰기
또는 읽기
요청
=> 핸들러 API
를 통해 MySQL 엔진과 데이터를 주고 받음
--얼마나 핸들러 API를 통해 많은 데이터 작업이 있었는지
show global status like 'Handler%';
MySQL 서버는 스레드 기반
으로 작동하며, 포그라운드 스레드
와 백그라운드 스레드
로 구분
--실행 중인 스레드 목록
select thread_id, name, type, processlist_user, processlist_host
from performance_schema.threads
order by type, thread_id;
마지막 one_connection
스레드만이 실제 사용자의 요청
을 처리하는 포그라운드 스레드
다.
클라이언트 수
만큼 존재쿼리 문장
처리스레드 캐시(Thread Cache)
로 돌아감대기 중 스레드
가 있으면 스레드 캐시에 넣지 않고 스레드 종료
thread_cache_size
로 설정데이터 버퍼
나 캐시
로부터 가져옴디스크
의 데이터나 인덱스 파일
로부터 데이터를 읽어와 작업 처리MyISAM
: 디스크 쓰기 작업
까지 포그라운드 스레드가 처리InnoDB
: 데이터 버퍼
나 캐시
까지만 포그라운드 스레드가 처리, 나머지 버퍼
로부터 디스크
까지 기록
하는 작업은 백그라운드 스레드가 처리병합
하는 스레드디스크로 기록
하는 스레드디스크에 기록
하는 스레드읽어
오는 스레드잠금
이나 데드락
을 모니터링하는 스레드글로벌 메모리 영역
: MySQL 서버가 시작되면서 운영체제
로부터 할당
하나의 메모리 공간
만 할당
=> 필요에 따라 2개 이상 메모리 공간 할당받더라도, 모든 스레드
에 의해 공유
MySQL 서버상에 존재하는 클라이언트 스레드
가 쿼리를 처리하는 데 사용하는 메모리 영역
세션
: 클라이언트와 MySQL 서버와의 커넥션
클라이언트가 MySQL 서버에 접속하면, MySQL 서버는 클라이언트 커넥션으로부터 요청을 처리하기 위해 스레드
를 하나씩 할당함
=> 클라이언트가 사용하는 메모리 공간이기 때문에 클라이언트 메모리 영역
이라고도 함
=> 세션 메모리 영역
이라고도 함
-- 지원되는 스토리지 엔진 확인
show engines;
플러그인 형태
의 스토리지 엔진은 쉽게 업그레이드 가능
-- 플러그인 내용 확인
show plugins;
플러그인 아키텍처
의 단점을 보완하기 위해 컴포넌트 아키텍처
지원
쿼리 파서
: 쿼리 문장
을 토큰
으로 분리해 트리 형태 구조
로 만들어냄
=> 기본 문법 오류
발견
전처리기
: 파서 트리
를 기반으로 쿼리 문장에 구조적인 문제점
확인
=> 칼럼 이름, 내장 함수, 테이블 등 개체를 매핑해 객체 존재 여부
와 객체 접근 권한
등 확인
옵티마이저
: 사용자의 요청으로 들어온 쿼리 문장을 저렴한 비용
으로 가장 빠르게
처리할지 결정
실행 엔진
: 만들어진 계획
대로 각 핸들러
에게 요청해서 받은 결과
를 또 다른 핸들러 요청
의 입력
으로 연결하는 역할
핸들러
: MySQL 실행 엔진
의 요청에 따라 데이터를 디스크
로 저장
하고, 디스크로부터 읽어
오는 역할
내부적으로 사용자의 요청을 처리하는 스레드 개수
를 줄여서
동시 처리되는 요청이 많더라도 MySQL 서버의 CPU가 제한된 개수
의 스레드 처리
에만 집중할 수 있게 해서 서버 자원 소모 줄이는
목적
데이터 딕셔너리/메타데이터
: 데이터베이스 서버에서 테이블
의 구조 정보
와 스토어드 프로그램
등의 정보
테이블의 구조 정보나 스토어드 프로그램의 코드 관련 정보를 모두 InnoDB 테이블
에 저장하고, 시스템 테이블
과 데이터 딕셔너리 정보
를 모두 모아 mysql DB
에 저장
=> 스키마 변경 작업 중간에 MySQL 서버가 비정상적으로 종료되어도 스키마 변경이 완전한 성공
또는 완전한 실패
로 정리
InnoDB
의 모든 테이블은 기본적으로 프라이머리 키
를 기준으로 클러스터링
되어 저장
=> 프라이머리 키 값의 순서
대로 디스크에 저장
=> 세컨더리 인덱스
는 레코드의 주소 대신 프라이머리 키의 값
을 논리적인 주소
로 사용
=> 오라클의 IOT
와 동일
외래 키
는 부모 테이블과 자식 테이블 모두 해당 칼럼
에 인덱스 생성
이 필요
--외래키 체크 일시적 해제
set foreign_key_checks =OFF;
언두 로그
를 이용
하나의 레코드에 대해 여러 개의 버전이 동시에 관리
MVCC
를 이용해 잠금을 걸지 않고 읽기 작업
수행
=> 변경되기 전
의 데이터를 읽기 위해 언두 로그
사용
내부적으로 잠금이 교착 상태
에 빠지지 않았는지 체크하기 위해 잠금 대기 목록을 그래프
형태로 관리
데드락 감지 스레드
: 주기적으로 잠금 대기 그래프
를 검사해 교착 상태에 빠진 트랜잭션들을 찾아 강제 종료
=> 동시 처리 스레드
가 많아지거나 트랜잭션이 가진 잠금
수가 많아지면 느려짐
=> innodb_dealock_detect
를 off로 설정하고, innodb_lock_wait_timeout
을 활성화해서 사용하면 성능이 더 좋아질 수 있음
innodb_force_recovery
시스템 변수를 설정해 MySQL 서버를 시작하면,
서버 시작시 innoDB 스토리지 엔진이 데이터 파일이나 로그 파일 손상 여부 검사 과정을 진행
=> 로그 파일
손상시 6
으로 설정하고 기동
=> 데이터 파일
손상시 1
로 설정하고 기동
테이블스페이스의 데이터
나 인덱스 페이지
에서 손상된 부분이 발견돼도 무시하고 서버 시작
=> 로그 파일에 Database page corruption on disk or a failed
출력시
=> mysqldump
나 select into outfile ..
을 사용해 데이터베이스 다시 구축
백그라운드 스레드
가운데 메인 스레드
시작하지 않고 MySQL 서버 시작
=> InnoDB는 트랜잭션 롤백
을 위해 언두 데이터
를 관리하는데, 트랜잭션이 커밋
되어 불필요한 언두 데이터는 메인 스레드
에 의해 주기적으로 삭제(Undo purge)
되는데, 해당 과정에서 장애 발생시
MySQL 서버는 다시 시작하면 언두 영역
의 데이터를 먼저 읽어 데이터 파일에 적용한 후, 다음 리두 로그
의 내용을 다시 덮어 써서 장애 시점
의 데이터 상태를 만들어냄
=> 정상적인 MySQL 서버 시작시 최종적으로 커밋되지 않은 트랜잭션
은 롤백
수행
=> 3으로 설정시, 커밋되지 않은 트랜잭션
작업을 롤백X
, 즉 커밋되지 않고 종료된 트랜잭션은 계속 그 상태로 남아있게 하는 모드
=> mysqldump
를 이용해 데이터를 백업하는 것이 좋음
InnoDB는 Insert, Delete, Update
등 데이터 변경
시 인덱스 변경 작업
을 상황에 따라 즉시 처리
할 수 있고, 인서트 버퍼
에 저장해두고 나중에 처리할 수도 있음
=> MySQL을 종료해도 병합
되지 않을 수 있는데, 만약 MySQL이 재시작되면서 인서트 버퍼 손상 감지
하면 MySQL 서버는 시작 못함
=> 인서트 버퍼
의 내용
을 무시하고 강제로 시작
=> 실제 데이터가 아니라 인덱스
와 관련된 부분이므로 데이터베이스 다시 구축시 데이터 손실 없음
MySQL 서버가 장애
나 정상적으로 종료
되는 시점에 진행 중인 트랜잭션
이 있다면 MySQL은 그냥 커넥션을 강제로 끊어버리고 별도의 정리 작업 없이 종료
=> 재시작시 InnoDB엔진은 언두 레코드
를 이용해 데이터 페이지를 복구하고, 리두 로그
를 적용해 종료 시점이나 장애 발생 시점 재현
=> 마지막으로 커밋되지 않은 트랜잭션
에서 변경한 작업 모두 롤백
=> 언두 로그 사용X시
MySQL 서버 실행 못함
=> 5로 시작시 언두 로그 무시
하고 시작
=> 종료되던 시점에 커밋되지 않았던 작업
도 모두 커밋된 것처럼 처리
InnoDB 스토리지 엔진의 리두 로그
손상시 서버 시작 불가
=> 리두 로그 모두 무시
한 채로 서버 시작
=> 커밋됐다 하더라도 리두 로그
에만 기록되고 데이터 파일에 기록X
인 데이터는 모두 무시
=> 즉 마지막 체크 포인트 시점
의 데이터만 남음
=> 기존 리두 로그 모두 삭제
하고 MySQL 서버 시작하는 것이 좋음
버퍼 풀
: 디스크의 데이터 파일
이나 인덱스 정보
를 메모리에 캐시
해 두는 공간
=> 변경 작업
을 모아서 처리하는 일괄 작업
역할도 같이 함
=> 버퍼 풀
을 쪼개어 여러 개의 작은 버퍼 풀(버퍼 풀 인스턴스)
로 나눔
버퍼 풀
이라는 거대한 메모리 공간을 페이지 크기
로 쪼개어 데이터를 필요로 할 때 해당 데이터 페이지
를 읽어서 각 조각에 저장
=> 페이지 크기 조각 관리
를 위해 LRU 리스트
, 플러시 리스트
, 프리 리스트
관리
프리 리스트
: InnoDB 버퍼 풀에서 실제 사용자 데이터로 채워지지 않은 비어 있는 페이지들
의 목록
=> 새롭게 디스크의 데이터 페이지를 읽어와야 하는 경우 사용
Old 서브리스트
: LRU
New 서브리스트
: MRU
InnoDB 스토리지 엔진
에서 데이터 찾는 과정
데이터 페이지
가 버퍼 풀
에 있는지 검사InnoDB 어댑티브 해시 인덱스
를 이용해 페이지 검색인덱스
를 이용해 버퍼 풀
에서 검색있다면
해당 페이지의 포인터를 MRU 방향
으로 승급디스크
에서 필요한 데이터 페이지를 버퍼 풀에 적재
하고, 적재된 페이지에 대한 포인터를 LRU 헤더
부분에 추가실제로 읽히면
MRU 헤더 부분
으로 이동나이
부여자주 접근
됐다면 해당 페이지의 인덱스 키
를 어댑티브 해시 인덱스
에 추가플러시 리스트
: 디스크로 동기화되지 않은 데이터를 가진 데이터 페이지(더티 페이지
)의 변경 시점 기준
의 페이지 목록 관리
=> 데이터 변경
이 가해진 데이터 페이지는 리스트에 관리
=> 체크포인트
를 발생시켜 리두 로그
와 데이터 페이지
동기화
버퍼 풀
은 클린 페이지
와 더티 페이지
를 가진다.
=> 더티 페이지
는 무한정 버퍼 풀
에 머무를 수 있는 것이 아님
=> InnoDB 스토리지 엔진에서 리두 로그
는 데이터 변경
이 계속 발생하면 리두 로그 파일
에 기록됐던 로그 엔트리는 어느 순간 다시 새로운 로그 엔트리
로 덮어 쓰임
전체 리두 로그 파일
에서 재사용 가능한 공간
과 당장 재사용 불가능한 공간
을 구분해서 관리
=> 활성 리두 로그
: 재사용 불가능한 공간
(화살표를 가진 엔트리들)
LSN(Log Sequence Number)
: 리두 로그 파일
의 공간은 계속 순환되어 재사용되지만 매번 기록
될 때마다 로그 포지션
은 증가한 값을 갖게 됨
=> InnoDB는 주기적으로 체크포인트
를 발생시켜 리두 로그
와 버퍼 풀
의 더티 페이지
를 디스크로 동기화
하는데, 가장 최근 체크포인트 지점
의 LSN
이 활성 리두 로그 공간 시작점
=> 체크포인트 에이지
: 가장 최근 체크포인트
의 LSN
과 마지막 리두 로그 엔트리
의 LSN
차이
체크포인트 발생
시 체크포인트 LSN
보다 작은
리두 로그 엔트리
와 관련된 더티 페이지
는 모두 디스크에 동기화
=> 리두 로그 엔트리
도 디스크로 동기화
더티 페이지 플러시
: 더티 페이지
를 디스크
에 동기화
InnoDB는 버퍼 풀
에서 아직 디스크
로 기록되지 않은 더티 페이지들을 악영향 없이
동기화하기 위해 2개의 플러시 기능
을 백그라운드
로 실행
플러시 리스트 플러시
: InnoDB가 주기적으로 플러시 함수
를 호출해서 플러시 리스트
에서 오래전에 변경된 데이터 페이지 순서대로
디스크에 동기화 작업 수행
=> 어댑티브 플러시
: 리두 로그의 증가 속도
를 분석해서 적절한 수준으로 더티 페이지
가 버퍼 풀
에 유지될 수 있도록 디스크 쓰기 실행
LRU 리스트 플러시
: 사용 빈도가 낮은 데이터 페이지
들을 제거해서 새로운 페이지들을 읽어올 공간을 만들어야 하는데, 이때 LRU 리스트 플러시 함수
사용
=> 스캔하면서 더티 페이지
는 디스크에 동기화
, 클린 페이지
는 즉시 프리 리스트
로 페이지를 옮김
워밍업(Warminig Up)
: 서비스 재부팅 시 디스크의 데이터가 버퍼 풀
에 적재돼 있는 상태
-- 서버 셧다운 전 버퍼 풀 상태 백업
set global innodb_buffer_pool_dump_now=ON;
-- 재시작 후, 백업된 버퍼 풀의 상태 복구
set global innodb_buffer_pool_load_now=ON;
--버퍼 풀 복구하는 과정이 어느 정도 진행됐는지 상태 값 제공
show status like 'Innodb_buffer_pool_dump_status'\G
-- 버퍼 풀 복구 도중 서비스 시작시 버퍼 풀 복구 멈춤
set global innodb_buffer_pool_load_abort=ON;
-- 테이블 인덱스별로 데이터 페이지가 얼마나 버퍼 풀에 적재 됐는지
select it.name table_name, ii.name index_name, ici.n_cahced_pages n_cached_pages
from information_schema.innodb_tables it
inner join information_schema.innodb_indexes ii on ii.table_id=it.table_id
inner join information_schema.innodb_cached_indexes ici on ici.index_id=ii.index_id
where it.name=concat('employees','/','employees');
파셜 페이지(Partial-page)
: 더티 페이지를 디스크 파일
로 플러시할 때 일부만 기록
되는 문제가 발생하면, 그 페이지 내용은 복구할 수 없을 수도 있는데, 페이지
가 일부만 기록
되는 현상
=> 하드웨어 오작동 또는 시스템 비정상 종료 등으로 발생
언두 로그
: DML로 변경되기 이전 버전
의 데이터
-- 언두 레코드 건수 확인
show engine innodb status \G
언두 테이블스페이스
: 언두 로그
가 저장되는 공간
=> 8.0.14 이상부터 항상 시스템 테이블스페이스 외부
의 별도 로그 파일
에 기록
=> 하나의
언두 테이블스페이스는 1개 이상 128개 이하
의 롤백 세그먼트
를 가짐
=> 롤백 세그먼트
는 1개 이상
의 언두 슬롯
을 가짐
-- 테이블스페이스 생성
create undo tablespace undo_03 add datafile '파일경로';
--비활성화
alter undo tablespace undo_03 set inactive;
--삭제
drop undo tablespace undo_03;
Undo table truncate
: 언두 테이블스페이스 공간을 필요한 만큼
만 남기고 불필요하거나 과도하게 할당된 공간을 운영체제로 반납하는 것
자동 모드
: 트랜잭션이 데이터 변경 시 언두 로그에 기록이 되는데, InnoDB 스토리지 엔진의 퍼지 스레드(Purge Thread)
는 주기적으로 깨어나 언두 로그 공간
에서 불필요해진 언두 로그
를 삭제하는 작업 실행언두 퍼지
수동 모드
: 언두 테이블스페이스
를 비활성화
해서 언두 테이블스페이스가 더이상 사용되지 않도록 설정하면 퍼지 스레드
는 비활성 상태
의 언두 테이블스페이스
를 찾아서 불필요한 공간 잘라내고
운영체제로 해당 공간 반납언두 테이블스페이스
가 최소 3개 이상은 돼야 작동인덱스
를 업데이트 하는 작업은 랜덤하게 디스크 읽는 작업
이 필요
=> 변경해야 할 인덱스 페이지가 버퍼 풀
에 있으면 바로 업데이트 수행
=> 디스크로부터 읽어와서 업데이트
해야 한다면 즉시 실행하지 않고 임시 공간에 저장
해 두고 바로 사용자에게 결과 반환
체인지 버퍼
: 위 경우 사용하는 임시 메모리 공간
사용자에게 결과를 전달하기 전에 반드시 중복 여부 체크
해야 하는 유니크 인덱스
는 체인지 버퍼 사용X
체인지 버퍼에 임시로 저장된 인덱스 레코드 조각
은 이후 백그라운드 스레드에 의해 병합
=> 체인지 버퍼 머지 스레드
--체인지 버퍼 사용중인 메모리 공간 크기
select event_name, current_number_of_bytes_used
from performance_schema.memory_summary_global_by_event_name
where event_name='memory/innodb/ibuf0ibuf';
리두 로그
: 영속성
과 가장 밀접하게 관련
리두 로그 아카이빙
: 데이터 변경이 많아서 리두 로그
가 덮어쓰인다
고 하더라도 백업 실패하지 않게
해줌
--리두 로그 아카이빙 시작
do innodb_redo_log_archive_start('디렉터리레이블', '서브디렉터리이름');
--종료
do innodb_redo_log_archive_stop();
MySQL 서버에서 트랜잭션이 커밋
돼도 데이터 파일
은 즉시 디스크로 동기화X
, 반면 리두 로그
는 항상 디스크로 기록
-- 리두 로그 비활성화
alter instance disable innodb redo_log;
--상태 변수 확인해 리두 로그 활성화/비활성화 확인
show global status like 'Innodb_redo_log_enabled';
어댑티브 해시 인덱스(Adaptive Hash Index)
: 사용자가 수동으로 생성하는 인덱스가 아니라 InnoDB 스토리지 엔진
에서 사용자가 자주 요청하는 데이터
에 대해 자동
으로 생성하는 인덱스
=> 인덱스 키 값
과 데이터 페이지 주소
의 쌍으로 관리
=> 인덱스 키 값
: B-Tree 인덱스 고유 번호
와 인덱스 실제 키 값
조합
=> 데이터 페이지 주소
: 버퍼 풀
에 로딩된 페이지의 주소
키 캐시/키 버퍼
: InnoDB의 버퍼 풀
과 비슷한 역할
=> 인덱스만
대상
=> 인덱스의 디스크 쓰기 작업
에 대해서만 부분적으로 버퍼링
Key_reads : 인덱스를 디스크에서 읽어 들인 횟수
Key_read_requests : 키 캐시로부터 인덱스 읽은 횟수
키 캐시 히트율 = 100-(Key_reads/Key_read_requests*100)
show global status like 'Key%';
--키 캐시 2GB 생성
kbuf_board.key_buffer_size=2GB
-- 게시판의 인덱스를 캐시
cache index db1.board, db2.board in kbuf_board;
MyISAM 스토리지 엔진은 디스크 I/O
에 관한 캐시
나 버퍼링
기능을 가지고 있지 않으므로 운영체제
의 캐시 기능
을 이용해야 함
InnoDB 스토리지 엔진
: 프라이머리 키
에 의해 클러스터링
되어 저장
MyISAM 테이블
: 프라이머리 키에 대한 클러스터링 없이 데이터 파일
이 힙 공간
처럼 활용
=> 프라이머리 키 값과 무관하게 INSERT되는 순서대로
데이터 파일에 저장
=> 프라이머리 키와 세컨더리 인덱스는 모두 데이터 파일에 저장된 레코드
의 ROWID
값을 포인터로 가짐
MySQL이 실행되는 도중 발생하는 에러
나 경고 메시지
가 출력되는 로그 파일
제너럴 쿼리 로그
: 슬로우 쿼리 로그
와는 조금 다르게 실행되기 전에 MySQL이 쿼리 요청
을 받으면 바로 기록
하기 때문에 실행 중 에러 발생
해도 일단 로그 파일에 기록
슬로우 쿼리 로그 파일
: long_query_time
시스템 변수에 설정한 시간 이상
의 시간이 소요된 쿼리가 모두 기록
=> 실제 소요된 시간
을 기준으로 슬로우 쿼리 로그에 기록할지 여부를 판단하므로 반드시 쿼리가 정상적으로 실행 완료
돼야 슬로우 쿼리 로그에 기록 가능