728x90
반응형
SMALL
InnoDB 스토리지 엔진 아키텍처
- MySQL에서 사용할 수 있는 아키텍처 중에 거의 유일하게 레코드 기반 잠금을 제공한다. 그 때문에 높은 동시성 처리가 가능하고 안정적이며 성능이 뛰어나다.
InnoDB 주요 특징
프라이머리 키에 의한 클러스터링
- 모든 테이블은 기본적으로 프라이머리 키를 기준으로 클러스터링되어 저장된다. 이 말은 프라이머리 키 값의 순서대로 디스크에 저장된다는 뜻이다.
- 모든 세컨더리 인덱스는 레코드의 주소 대신 프라이머리 키 값을 논리적인 주소로 사용한다. 프라이머리 키가 클러스터링 인덱스이기 때문에 프라이머리 키를 이요한 레인지 스캔은 더욱 더 빨리 처리 될 수 있다.
- 쿼리의 실행 계획에서 프라이머리 키는 기본적으로 다른 보조 인덱스에 비해 가중치가 높게 설정된다.
- 오라클의 DBMS에서 사용하는 IOT와 동일한 구조가 MySQL의 InnoDB에서는 일반적인 테이블 구조가 될 수 있다.
- MyISAM 스토리지 엔진에서는 클러스터링 키를 지원하지 않기 때문에 구조적으로 세컨더리 인덱스와 프라이머리 키는 아무런 차이가 없다.
외래 키 지원
- MyISAM이나 MEMORY 테이블에서는 지원하지 않지만 InnoDB 스토리지 엔진에서는 지원한다.
- 외래 키는 데이터베이스서버 운영시에는 불편함 때문에 사용하지 않는 경우도 있지만 개발 환경에서는 중요한 가이드 역할을 한다.
- 부모 테이블과 자식 테이블에 데이터가 있는지 체크하는 작업이 필요하므로 잠금이 여러 테이블로 전파되며, 그로 인한 데드락이 발생할 수 있기 때문에 외래 키의 존재를 주의해야한다.
- 외래 키가 존재한다면 데이터를 직접 적재하거나 스키마 변경 등의 작업이 실패할 수 있다. 부모 테이블과 자식 테이블의 관계를 잘 알고 있다면 순차적으로 처리할 수는 있지만 외래 키가 복잡하게 얽힌 경우 이러한 작업을 하기 힘들다.
- 이 경우에는 foreign_key_checks 시스템 변수를 OFF로 설정하면 외래 키 관계에 대한 체크 작업을 일시적으로 멈출 수 있다. 이 경우에는 레코드 적재, 삭제등 부가적인 작업도 손쉽게 처리 가능하다. 이 때 부모 테이블에 대한 작업(On Delete Cacade, On Update Cascade)도 무시하게 된다.
- 하지만 부모와 자식 테이블 관계는 깨진 상태로 두면 안되기에 다시 외래 키 체크 기능을 활성화해야 한다.
MVCC(Multi Version Concurrency Control)
- 레코드 레벨의 트랜잭션을 지원하는 DBMS가 제공하는 기능이다.
- 가장 큰 목적은 잠금을 사용하지 않는 일관된 읽기가 가능하다는 점이다. 이 때 InnoDB는 Undo Log를 이용해서 이 기능을 구현한다.
- 이때 다루는 멀티 버전은 하나의 레코드에 대해 여러 개의 버전이 동시에 관리된다는 의미이다. 이 내용은 면접때 자주 나오는 격리 수준과 함께 연관되며 InnoDB는 기본적으로 Read_Committed로 되어 있다.
- 예시 코드와 그림을 보며 이해해보자.
CREATE TABLE MEMBER(
M_ID INT NOT NULL,
M_NAME VARCHAR(20) NOT NULL,
M_AREA VARCHAR(100) NOT NULL,
PRIMARY KEY (M_ID),
INDEX IX_AREA(M_AREA)
);
INSERT INTO MEMBER(M_ID, M_NAME, M_AREA) VALUES (12, '홍길동', '서울');
COMMIT;
1. Insert 문이 실행되면 아래와 같은 상태로 바뀌게 된다.
2. Update 문장이 실행될때 처리 절차
- Update 문장이 실행되면 커밋 실행 여부와 관계없이 InnoDB의 버퍼 풀은 새로운 값인 '경기'로 업데이트 된다.
- 디스크의 데이터 파일에는 체크포인트나 InnoDB의 Write 쓰레드에 의해 새로운 값으로 업데이트 돼 있을 수도 있고 아닐 수도 있다. InnoDB가 ACID를 보장하기 때문에 일반적으로 InnoDB의 버퍼 풀과 데이터 파일은 동일한 상태라고 볼 수 있다.
- 이 상태에서 SELECT구문으로 id=12인 유저를 찾게 되면 시스템 변수에 설정된 격리 수준에 따라 반환되는 내용이 다르다. Read Committed 상태나 그 이상이라면 아직 커밋되지 않았기 때문에 InnoDB버퍼 풀이나 데이터 파일에 있는 내용 되신 변경되기 이전의 내용을 보관하고 있는 언두 영역의 데이터를 반환한다. 이 과정이 DBMS에서의 MVCC라고 한다. 하나의 레코드에 대해 2개의 버전이 존재하고 필요에 따라 데이터가 보여지는 상황이 달라지는 구조이다.
- 예전 버전의 데이터는 무한히 많아질 수 있다. 트랜잭션이 길어지면 언두에서 관리하는 예전 데이터가 삭제되지 못하고 오랫동안 관리되어야 하며, 자연히 언두 영역이 저장되는 시스템 테이블 용량이 많이 늘어나게 된다.
- 이 과정에서 Commit을 하게 되면 InnoDB는 더 이상 변경 작업 업싱 현재 상태를 영구적인 상태로 만든다. 하지만 롤백을 하게 되면 InnoDB는 Undo Log에 있는 백업 데이터를 InnoDB로 복구시키고, Undo 영역을 삭제 시킨다. 커밋이 된다고 Undo 영역이 바로 삭제되는 것은 아니다. 필요로 하는 트랜잭션이 없을때 영구적으로 삭제 된다.
UPDATE MEMBER SET M_AREA='경기' WHERE M_ID = 12;
잠금 없는 일관된 읽기(Non-Locking Consistent Read)
- InnoDB 스토리지 엔진은 MVCC 기술을 사용해서 잠금을 걸지 않고 읽기 작업을 수행한다. 잠금을 걸지 않기 때문에 InnoDB에서 ㅇ릭기 작업은 다른 트랜잭션이 가지고 있는 잠금을 기다리지 않고, 읽기 작업이 가능하다.
- 격리 수준이 SERIALIZABLE이 아닌 Read_Uncommitted, Read Committed,Repeatable Read수준인 경우 삽입되지 않은 순수한 읽기 작업은 다른 트랜잭션의 변경 작업과 관계없이 항상 잠금을 대기하지 않고 바로 실행된다.
- 오랜 시간 동안 활성상태인 MySQL 서버가 느려진다면 일관된 일긱를 위해 로그를 삭제하지 못하고 계속 유지하는 언두 로그가 원인일수도 있다.
자동 데드락 감지
- InnoDB 스토리지 엔진은 내부적으로 잠금이 교착상태에 빠지지 않았는지 체크하기 위해 Wait-for-list 형태로 관리한다.
- 교착 상태에 빠진 트랜잭션중 하나를 강제로 종료하게 되는데 종료하는 트랜잭션 기준은 언두 로그 레코드를 더 적게 가진 트랜잭션이다. 트랜잭션이 언두 레코드를 적게 가졌다는 이야기는 롤백을 해도 언두 처리를 해야 할 내용이 적다는 것이기에 강제 롤백으로 인한 서버의 부하도 덜 유발하기 때문이다.
- 일반적인 서비스에서는 데드락 감지 스레드가 트랜잭션의 잠금 목록을 검사해서 데드락을 찾아내는 작업은 크게 부담되지 않지만 동시 처리 스레드가 많아 지면 데드락 가밎 스레드가 느려진다. 잠금 목록을 검사해야 하기 때문에 잠금 목록이 저장된 리스트에 새로운 잠금을 걸도 데드락 스레드를 찾게 된다. 데드락 감지 스레드가 느려지면 서비스 쿼리를 처리 중인 스레드는 더는 작업을 진행하지 못하고 대기하면서 서비스에 악영향을 미치게 된다. 이렇게 동시 처리 스레드가 매우 많은 경우 더 많은 CPU 자원을 소모할 수 있다.
이 문제를 해결 하기 우해 innodb_deadlock_detect 시스템 변수를 별도로 제공하며 이를 off로 설정하면 더 이상 데드락 감지 스레드가 작동하지 않게 된다. 하지만 off상태라면 데드락 상태를 감지하지 못하기 때문에 무한정 대기상태에 이른다. 이를 해결하기 위해 innodb_lock_wait_timeout 시스템 변수를 활성화하면 이런 데드락 상황에서 일정 시간이 지나면 자동으로 요청이 실패하고 에러 메세지를 반환하게 된다. 그러므로 innodb_deadlock_detect를 off로 설정한 경우라면 innodb_lock_wait_timeout을 기본값인 50초보다 훨씬 낮은 시간으로 변경해서 사용하는 것을 권장하고 있다.
구글에서는 PK 기반 조회 및 변경이 아주 높은 빈도로 실행되는 서비스가 많았는데, 이렇게 많은 트랜잭션을 동시에 실행하는 서비스는 데드락 감지 스레드가 성능을 상당히 저하시키는 것을 알아냈다. 만약 PK 또는 세컨더리 인덱스를 기반으로 높은 동시성 처리를 요구하는 서비스가 있다면 innodb_deadlock_detect를 비활성화해서 성능 비교를 해보는 것도 추천한다.
728x90
반응형
SMALL
'면접 준비 > DB' 카테고리의 다른 글
SubQuery와 Join 절 성능 분석 (1) | 2025.05.22 |
---|---|
[DB] PhantomRead란? (0) | 2025.03.02 |
[DB / Transaction] 트랜잭션 고립 수준 (0) | 2025.03.02 |
[DB / JPA] 낙관적 락과 비관적 락 (0) | 2025.03.02 |
[CS 면접 질문 - DB] 파티셔닝 (0) | 2025.01.06 |