InnoDB는 테이블 기반의 잠금이 아닌 레코드 기반의 잠금을 제공한다. 그때문에 높은 동시성 처리가 가능하고 안정적이며 성능이 뛰어나다.
PK에 의한 클러스터링
InnoDB의 모든 테이블은 PK 값의 순서대로 클러스터링되어 저장된다. 모든 세컨더리 인덱스는 레코드 주소 대신 PK의 값을 논리적인 주소로 사용한다. 테이블이 PK 순서대로 저장되어 있기 때문에 PK를 이용한 레인지 스캔이 상당히 빠르다. 결과적으로 실행계획에서 PK는 기본적으로 다른 보조 인덱스에 비해 비중이 높게 설정된다.
MVCC(Multi Version Concurrency Control)
잠금을 사용하지 않는 일관된 읽기 제공을 위해 하나의 레코드에 대해 여러 개의 버전이 동시에 관리된다. InnoDB는 언두 로그를 이용해 이 기능을 구현한다. 아래 예시를 보자.
다음 삽입 쿼리를 실행하면 데이터 베이스는 다음과 같은 상태로 바뀐다.
INSERT INTO member (id, name, area) VALUES (12, '홍길동', '서울');
UPDATE 문으로 area를 경기로 변경하면 다음과 같이 변화한다.
UPDATE member SET area='경기' WHERE id=12;
- InnoDB 버퍼 풀은 변경과 동시에 데이터가 변경된다.
- 변경된 칼럼의 이전 값만 언두 로그에 복사한다.
- 디스크의 데이터 파일에는 체크포인트나 Write 스레드에 의해 업데이트될 것이다.(ACID를 보장하기 때문에 일반적으로 InnoDB 버퍼 풀과 동일한 상태라 봐도 무방)
만약 다른 사용자가 COMMIT이나 ROLLBACK되지 않은 상태에서 작업 중인 레코드를 조회하면 어떤 데이터를 조회할까?
이는 MySQL 서버에 설정된 격리 수준에 따라 다르다.
- READ_UNCOMMITTED인 경우 InnoDB 버퍼 풀이나 디스크로 부터 변경된 데이터를 반환한다.
- READ_COMMITTED인 경우 언두 영역의 데이터에서 변경되기 이전 데이터를 반환한다.
즉, 하나의 레코드에 대해 2개의 버전이 유지되고, 필요에 따라 어느 데이터가 보여지는지 달라지는 구조이다.
COMMIT 명령을 실행하면 더 이상의 변경 작업 없이 지금 상태를 영구적인 데이터로 만든다. 하지만 ROLLBACK을 실행하면 언두 영역에 있는 백업 데이터를 InnoDB 버퍼 풀로 복구하고, 언두 영역의 내용을 삭제한다. 커밋이 된다고 언두 영역의 백업 데이터가 항상 바로 삭제되지 않는다. 언두 영역을 필요로 하는 트랜잭션이 더는 없을 때 비로소 삭제된다.
잠금 없는 일관된 읽기(Non-Locking Consistent Read)
InnoDB는 MVCC 기술을 이용해 잠금을 걸지 않고 읽기 작업을 수행할 수 있다. 순수한 읽기 작업은 다른 트랜잭션의 변경 작업과 관계 없이 항상 잠금을 대기하지 않고 바로 실행된다. 이때 변경되기 이전의 값을 언두 로그 를 통해 읽는다.
오랜 시간 활성화된 트랜잭션으로 인해 MySQL 서버가 느려지거나 문제가 발생할 수 있다. 이는 일관된 읽기를 위해 언두 로그를 삭제하지 못하고 유지하기 때문이다. 그래서 트랜잭션이 시작되면 빠르게 롤백이나 커밋을 통해 트랜잭션을 종료하는 것이 좋다.
자동 데드락 감지
데드락 상태를 체크하기 위해 잠금 대기 목록을 그래프 형태로 관리한다. 데드락 감지 스레드는 주기적으로 그래프를 검사해 교착 상태에 빠진 트랜잭션을 찾아 그 중 하나를 종료시킨다. 종료의 기준은 트랜잭션 언두 로그의 양이다. 언두 로그 레코드를 더 적게 가진 트랜잭션이 일반적으로 롤백의 대상이 된다.
참고로 MySQL 엔진의 테이블 잠금은 확인이 불가하여 데드락 감지가 불확실할 수 있다. innodb_table_locks를 활성화하면 테이블 레벨의 잠금도 감지가 가능하다. 일반적인 경우 해당 시스템 변수를 활성화 하자.
매우 많은 트랜잭션이 동시에 실행될 경우 트랜잭션이 가진 잠금이 많아진다. 감지해야하는 잠금이 많아지므로 데드락 감지 스레드의 속도가 느려질 수 있다. 데드락 스레드가 느려지면 서비스 쿼리를 처리하는 스레드도 함께 느려져 서비스에 악영향을 미친다. 이러한 문제점을 해결하기 위해 데드락 감지 스레드를 중지시킬 수 있다. innodb_deadlock_detect를 OFF 하면 된다. 데드락을 감지하지 못하지만 innodb_lock_wait_timeout 값을 활성화하여 데드락 상황이 일정 시간 유지되면 자동으로 요청을 실패하게 할 수 있다. innodb_lock_wait_timeout 기본값인 50초보다 훨씬 낮은 시간으로 변경해 사용할 것을 권장한다.
'Database' 카테고리의 다른 글
[MySQL] InnoDB 스토리지 엔진 아키텍처 - 3 (0) | 2023.02.08 |
---|---|
[MySQL] InnoDB 스토리지 엔진 아키텍처 - 2 (2) | 2023.02.01 |
[MySQL] MySQL 쿼리 실행 구조 (0) | 2023.01.10 |
[MySQL] MySQL 아키텍처 (0) | 2023.01.06 |
[DB] SQL JOIN (0) | 2023.01.06 |