InnoDB
- MySQL 스토리지 엔진 중 하나.
- 스토리지 엔진 중 거의 유일하게
레코드 기반의 잠금
제공한다.
- 높은 동시성 처리가 가능하고 안정적이며 성능이 뛰어나다.
PK에 의한 클러스터링
- PK를 기준으로 클러스터링되어 저장
- PK 값의 순서대로 디스크에 저장
- 세컨더리 인덱스는 레코드의 주소 대신 PK를 논리적인 주소로 사용
- PK가 클러스터링 인덱스이기 떄문에 PK를 이용한 Range Scan은 빠르게처리된다.
- 실행 계획에서 PK는 다른 보조 인덱스에 비해 비중이 높게 설정된다.
MVCC
- 잠금을 사용하지 않는 일관된 읽기를 제공
- Undo log를 이용해 이 기능을 구현
업데이트 과정
- Undo 로그에 원본 저장 후 InnoDB 버퍼 풀의 데이터를 업데이트
- 격리 수준에 따라 어느 영역에 있는 데이터를 읽을지 결정
- READ_COMMITED 이상 ⇒ Undo 영역
자동 데드락 감지
- InnoDB는 잠금이 교착 상태에 빠지지 않았는지 체크하기 위해
잠금 대기 목록
을 그래프
(Wait-for-List)형태로 관리한다.
- 데드락 감지 스레드를 가지고 있어, 주기적으로 그래프를 검사해 교착 상태에 빠진 트랜잭션들을 찾아서
그 중 하나를 강제 종료
한다.
- 종료될 쓰레드를 판단하는 기준은
Undo Log 양
이며,
Undo Log 레코드를 더 적게 가진 트랜잭션이 일반적으로 롤백의 대상이 된다.
- 롤백을 해도 Undo 처리를 해야 할 내용이 적으며, 부하도 덜 유발한다.
- InnoDB에서는 MySQl 엔진에서 관리되는 테이블 잠금을 볼 수가 없어
Dead Lock 감지가 불확실할 수 있다.
innodb_table_locks
을 활성화하면, 테이블 레벨의 잠금까지 감지할 수 있다.
- Dead Lock을 감지할 때, 잠금 상태가 변경되지 않도록 잠금 테이블에
새로운 잠금
을 걸고 데드락 스레드를 찾게 된다.
이때 작업 중이던 스레드
는 더는 작업을 진행하지 못해 서비스에 악영향을 미칠 수 있다.
innodb_deadlock_detect
를 OFF하면, Dead Lock 감지 스레드는 작동하지 않는다.
innodb_lock_wati_timeout
을 설정하면, 자동으로 요청을 실패하게 된다.
💡 Dead Lock 감지 스레드가 부담되어 OFF할 경우, timeout을 50초보다 훨씬 낮은 시간으로 사용하라
InnoDB 버퍼 풀
용도
- 데이터 캐시
- 쓰기 지연
- 디스크의 데이터 파일이나 인덱스 정보를 메모리에 캐시해 두는 공간이다.
- 쓰기 작업을 지연시켜 일괄 작업으로 처리할 수 있게 해주는 버퍼 역할도 같이 한다.
- 랜덤한 디스크 작업 발생을 버퍼 풀이 모아서 처리한다.
버퍼 풀 크기 설정
- 레코드 버퍼가 상당한 메모리를 사용하기도 한다.
- 클라이언트 세션에서 테이블의 레코드를 읽고 쓸 때 버퍼로 사용하는 공간
- Connection이 많고 사용하는 테이블도 많으면 용량이 커질 수 있다.
- 5.7버전 부터 InnoDB 버퍼 풀의
크기를 동적으로 조절
할 수 있게 되었다.
- 작은 값으로 설정해서 증가시켜 가는 방법이 최적이다.
- 메모리가 8GB 미만이라면, 50%를 InnoDB 버퍼 풀로 설정하는 것이 좋다.
innodb_buffer_pool_size
버퍼 풀 구조
- 메모리 공간을 페이지 크기의 조각으로 쪼개어 InnoDB 스토리지 엔진이 데이터를 필요로 할 때 해당 데이터 페이지를 읽어서 각 조각에 저장한다.
- 크기 조각을 관리하기 위해 LRU, Flush, Free 3개의 자료 구조를 관리한다.
- Free List : 실제 사용자 데이터로 채워지지 않은 비어 있는 페이지 목록
플러시 리스트
- 디스크로 동기화되지 않은 데이터를 가진 데이터 페이지의
변경 시점 기준의 페이지 목록
- 읽은 후 변경이 없다면 플러시 리스트에 관리되지 않지만, 변경이 가해진 데이터 페이지는 플러시 리스트에 관리된다.
Redo Log → 디스크, 데이터 페이지가 디스크로 기록됐다는 것은 보장 X
- 체크포인트를 발생시켜 Redo Log와 데이터 페이지의 상태를 동기화
- 체크포인트는 Redo Log의 어느 부분부터 복구를 실행해야 할지 판단하는 기준점
LRU 캐시 전략
변형된 LRU( Least Recently Used Algorithm ) 사용
기존
- Head에 가장 최신, Tail에 오래된 데이터 ⇒ 후에 Tail 제거
MySQL
- New, Old의 Sublist로 나눠지며 각 각 Head와 Tail 존재
- New의 Tail과 Old의 Head가 만나는 지점을
Mid Point
새로운 페이지가 들어올 경우 Old의 Head에 저장
단순한 LRU구조의 경우 Where절 없는 Select와 같은 일회성 데이터
를 가져올 경우 자주 사용되던 페이지가 제거될 수 있다.
- Old의 Head로 이동하면, 사용되지 않을 경우 빠르게 버퍼 풀에서 제거된다.
- 다시 참조될 경우 New의 head로 이동시켜 보관한다.
버퍼 풀과 Redo Log
-
버퍼 풀은 데이터 캐시
와 쓰기 버퍼링
이라는 두 가지 용도가 있다.
-
단순히 메모리 공간을 늘리는 것은 데이터 캐시 기능만 향상
시킨다.
-
쓰기 버퍼링 기능을 향상시키기 위해서는 리두 로그와의 관계를 파악해야 한다.
-
변경되지 않은 클린 페이지
와 변경된 데이터를 가진 더티 페이지
도 가지고 있다.
- 더티 페이지는 언젠가는 디스크에 기록돼야 한다.
-
Redo Log는 고정 크기 파일을 연결해서 순환 고리처럼 사용한다.
데이터 변경이 계속 발생하면 Redo Log 파일에 다시 새로운 로그 엔트리로 덮어 쓰인다.
재사용 가능한 공간
과 당장 재사용 불가능한 공간
을 구분해 관리해야 한다.
- 재사용 불가능한 공간을
활성 리두 로그(Active Redo Log)
라고 부른다.
-
Redo Log File의 공간은 매번 기록될 때마다 로그 포지션은 계속 증가된 값을 갖는다.
⇒ LSN (Log Sequence Number)
-
주기적으로 체크포인트 이벤트
를 발생시켜 Redo Log와 버퍼 풀의 더티 페이지를 디스크로 동기화
- 가장 최근 체크포인트 지점의 LSN이 활성 리두 로그 공간의 시작점
- 최근 체크포인트의 LSN과 마지막 Redo Log 엔트리의 LSN 차이를
Checkpoint Age
라고 한다.
Checkpoint Age는 Active Redo Log 공간의 크기이다.
-
버퍼 풀에 더티 페이지의 비율이 너무 높은 상태에서 갑자기 버퍼 풀이 필요해지는 상황이 오면 매우 많은 더티 페이지를 한 번에 기록해야 하는 상황이 온다.
-
버퍼 풀의 크기가 100GB 이하의 서버에서는 Redo Log File의 전체 크기를 대략 5~10GB 수준으로 선택하고 필요할 때마다 조금씩 늘려가면서 최적값을 선택하는 것이 좋다.
버퍼풀 상태 백업 및 복구
- 디스크의 데이터가 버퍼 풀에 적재되지 않았을 경우 몇십 배의 쿼리 속도가 차이난다.
- 5.5 버전에서는 강제 워밍업을 위해 주요 테이블과 인덱스에 대해 풀 스캔을 한 번씩 실행하고 서비스를 오픈했었다.
- 5.6 버전부터는 버퍼 풀 덤프 및 적재 기능이 도입됐다.
innodb_buffer_pool_dump_now
를 이용해 현재 버퍼 풀의 상태를 백업할 수 있다.
innodb_buffer_pool_load_now
⇒ 복구
- 자동으로 하는 방법
innodb_buffer_pool_dump_at_shutdown
,innodb_buffer_pool_load_at_startup