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