아래 포스팅과 이어집니다.
☁️ 어댑티브 해시 인덱스
InnoDB는 내부적으로 자주 사용되는 데이터에 페이지 대해 자동으로 생성되는 인덱스를 가지고 있다. 보통 테이블 인덱스라고 하면 B-Tree 구조로 생성하는데, 어댑티브 해시 인덱스는 이름처럼 해시 값을 가진다.
B-Tree 인덱스는 O(logN)의 빠른 탐색 속도를 가지지만, 수천 개의 스레드가 동시에 이를 처리한다면 속도는 느려질 수 밖에 없다. 이러한 상황에서 자주 사용되는 데이터 페이지를 빠르게 찾기 위해 어댑티브 해시 인덱스가 존재하는 것이다.
어댑티브 해시 인덱스는 버퍼 풀에 로딩된 페이지 주소를 가리킨다. 즉, 버퍼 풀에서 해당 데이터 페이지가 없어진다면 어댑티스 해시 엔덱스에서도 해당 페이지 정보가 사라진다.
Read MySQL에서는 어댑티브 해시 인덱스의 존재와 성능의 상관관계를 아래와 같이 소개한다.
성능 향상에 크게 도움이 되지 않는 경우
- 디스크 읽기가 많은 경우
- 특정 패턴의 쿼리가 많은 경우 (조인이나 LIKE 패턴 검색)
- 매우 큰 데이터를 가진 테이블의 레코드를 폭넓게 읽는 경우
반대로 성능 향상에 많은 도움이 되는 경우
- 디스크의 데이터가 InnoDB 버퍼 풀 크기와 비슷하 경우 (디스크 읽기가 많지 않은 경우)
- 동등 조건 검색(동등 비교와 IN 연산자)이 많은 경우
- 쿼리가 데이터 중에서 일부 데이터에만 집중되는 경우
☁️ 프라이머리 키에 의한 클러스터링
클러스터링이란 유사한 성격을 지닌 데이터를 한 데 묶는 것을 의미한다. 즉, InnoDB는 PK를 기준으로 묶여 키 값의 순선대로 디스크에 저장된다. PK외의 세컨더리 인덱스(유니크 키, index)는 PK의 값을 논리적인 주소로 사용한다. 따라서 PK를 이용한 범위 스캔은 다른 세컨더리 인덱스보다 상당히 빨리 처리될 수 있다. 옵티마이저는 세컨더리 인덱스보다 PK로 처리할 수 있는 실행 계획에 더 비중을 두어 고려한다고 한다.
☁️ 외래키
InnoDB에서는 외래키(FK) 기능을 제공한다. 외래 키는 테이블 간의 참조를 지원함으로 각 테이블 간 데이터의 일관성을 유지하는데 도움을 준다. InnoDB에서 외래키는 부모 테이블과 자식 테이블 모두 인덱스 생성이 필요하고, 변경 시 반드시 부모 혹은 자식 테이블에 데이터가 있는지 확인하는 추가 작업이 필요하다. 이는 잠금이 여러 테이블로 전파된다는 의미이고, 데드락 발생 가능성이 높아져 사용에 유의해야 한다.
아래의 명령어를 통해 외래 키 체크를 일시적으로 멈출 수 있다.
mySQL> SET foreign_key_checks=OFF;
— // 작업 실행
MySQL> SET foreign_key_checks=ON;
- <Real MySQL 8.0 1> (백은빈.이성욱 지음) 중에서
☁️ MVCC (Multi Version Cuncurrency Control)
직역하면 병행 제어를 위한 다중 버전 지원이다. 여기서 다중 버전이란 레코드가 여러 버전의 데이터를 가진다는 것을 의미한다.
일반적으로 레코드 레벨의 트랜잭션을 지원하는 DBMS가 지원하는 기능이며, MVCC의 가장 큰 목적은 잠금을 사용하지 않는 일관된 읽기를 지원하는 것이다.
잠금
을 사용하지 않고 레코드 기반의 일관된 읽기
를 지원하는 기술이다.
InnoDB는 언두 로그(Undo log)라는 메모리를 가지고 있는데, 쓰기 트랜잭션이 버퍼 풀에 존재하는 레코드 데이터의 변경을 수행하면 변경 이전의 데이터를 언두 로그에 저장함(레코드 전체가 저장되지 않고, PK와 변경된 컬럼에 한해 저장됨)으로써 MVCC를 지원한다.
쓰기 트랜잭션이 아직 커밋되지 않은 시점에 읽기 트랜잭션이 해당 레코드에 대한 읽기 작업을 수행한다면 어떻게 될까.
아직 커밋되지 않은 버퍼 풀의 데이터를 읽거나, 변경 이전의 언두 로그의 데이터를 읽을 것이다. 어떤 영역의 데이터를 읽을지는 트랜잭션의 isolation level
(격리 수준)에 따라 달라진다. isolation level에 대해서는 다음에 자세하게 정리해보도록 하겠다. (격리 수준에는 Read-Uncommited, Read-Commited, Repeatable Read, Serializable이 있다. MySQL은 기본적으로 Read-Commited 전략을 따른다.)
정리하자면 레코드에 대해 2개의 버전을 관리하며 읽기 작업에 따라 어떤 데이터를 보여줄 지 결정하는 구조를 MVCC라고 한다.
☁️ 잠금 없는 일관된 읽기 (Non-Locking Consitent Read)
InnoDB에서 MVCC를 지원하기에 읽기 작업은 다른 트랜잭션이 가지고 있는 잠금을 기다릴 필요 없이 (buffer pool 혹은 undo log 데이터의)읽기 작업이 가능하다.
격리 수준이 Serializable이 아닌 경우 읽기(insert와 연결되지 않은 순수한 select 작업만을 의미)작업은 다른 트랜잭션의 변경 작업에 관계없이 잠금을 대기하지 않고 실행된다. Serializable level의 경우 읽기 작업에 대해서도 다른 트랜잭션의 락을 기다려야 한다.
오랜 시간 동안 활성 상태인 트랜잭션으로 인해 MySQL 서버가 느려지거나 문제가 발생할 때가 가끔 있는데, 바로 이러한 일관된 읽기를 위해 언두 로그를 빨리 삭제하지 못하고 계속 유지해야 하기 때문에 발생하는 문제이다. 따라서 트랜잭션이 시작됐다면 가능한 빨리 롤백이나 커밋을 통해 트랜잭션을 완료하는 것이 좋다.
☁️ 자동 데드락 감지
InnoDB는 내부적으로 잠금이 교착 상태에 빠지지 않았는지 체크하기 위해 잠금 대기 목록을 그래프 형태로 관리한다. 내부의 데드락 감지 스레드가 주기적으로 해당 그래프를 검사하여 교착 상태에 빠진 트랜잭션 중 하나를 종료하는데 그 기준은 트랜잭션의 언두 로그 양이다.
'Database > Real MySQL' 카테고리의 다른 글
MySQL 엔진과 InnoDB 엔진의 잠금 (1) | 2024.01.27 |
---|---|
트랜잭션(Transaction)과 격리 수준(Isolation level) (1) | 2024.01.27 |
InnoDB 스토리지 엔진 - 버퍼 풀과 리두, 언두 로그 (1) | 2024.01.26 |
쿼리 실행 구조와 MySQL 8.0 변경점 (2) | 2024.01.25 |
MySQL 아키텍처와 스레딩 (2) | 2024.01.25 |