MySQL의 스토리지 엔진인 InnoDB의 전체적인 아키텍처이다. 이제부터는 InnoDB의 여러 주요 특징들에 대해 살펴볼 것이다.
모든 테이블은, 기본적으로 프라이머리 키를 기준으로 클러스팅되어 저장된다.
https://www.geeksforgeeks.org/difference-between-clustered-and-non-clustered-index/
클러스터링 인덱스는 테이블 당 하나만 존재할 수 있으며, 실제 블럭 주소를 참조하고 있다. 따라서 곧 바로 data block
에 접근할 수 있기 때문에 찾아가는데 여러 과정이 일어나는 Non-clustered 인덱스(Secondary Index)
보다 동작이 빠르다.
즉, PK 값 순서대로 디스크에 저장되기 때문에 테이블 내 데이터 자체가 인덱스가 된다. PK가 클러스터링 인덱스이기 때문에 레인지 스캔 속도가 매우 빠르다.
"특정 컬럼을 PK 로 지정한다." 라는 것은 곧 "해당 컬럼에 클러스터드 인덱스가 생성된다." 를 의미한다.
반면, MyISAM
스토리지 엔진에서는 PK와 세컨더리 인덱스는 구조적으로 아무런 차이가 없어 단순히 유니크 제약을 가진 세컨더리 인덱스가 될 뿐이다.
주로 실제 운영용 DB에서는 불편함 때문에 외래 키를 사용하지 않지만, 개발 환경에서는 좋은 가이드 역할을 해줄 수 있다.
만약 스키마를 변경하거나 수동으로 데이터를 적재하고자 할 때 테이블과 외래키가 복잡하게 얽혀 있다면 일시적으로 foreign_key_checks
옵션을 통해 외래 키 관계에 대한 체크 작업을 멈출 수 있다. 당연히, CASCADE
옵션도 해당 시간동안 동작하지 않는다.
SET foreign_key_checks=OFF;
SET SESSION foreign_key_checks=OFF;
하지만 부모 테이블에서 FK를 삭제했는데 자식 테이블에서 해당 레코드가 남아있을 수 있기 때문에 반드시 일관성을 맞추어 주어야 한다.
MySQL는 ACID와 같이 원자적이고 독립적인 수행을 보장하기 위해 트랜잭션에 대한 락을 제공한다. 하지만, 이제부터 설명할 MVCC
는 잠금을 사용하지 않고 일관된 READ
연산을 가능하게 한다.
Q. 락을 사용하지 않는 이유
공유 락과 베타적 락 모두 전체 레코드에 대한 잠금이 들어간다. 따라서 다른 트랜잭션이 보유중인 락을 기다리는 시간이 생기는데, 이 시간을 제거해 읽기 작업의 성능을 향상시킬 수 있다.
InnoDB는 락이 해주던 안전 잠금 장치 역할을, 언두 로그(Undo Log)를 이용해 MVCC를 구현하고 있다.
언두 로그란, 데이터에 변경이 일어나기 이전 원본을 복사해놓는 공간을 의미한다.
COMMIT
이 일어나면 현재 상태를 영구적인 데이터로 만들고, ROLLBACK
을 수행하면 언두 영역의 백업된 데이터를 InnoDB
버퍼 풀로 다시 복구한다. 그리고 기존 언두 영역의 데이터들은 언두 영역을 더 이상 필요로 하는 트랜잭션이 없을 때 모두 삭제된다.
예를 들어 아래의 그림을 보면 m_area
값을 서울에서 경기로 바꿨을 때, UPDATE
문장이 실행되면 커밋 실행 여부와 관계 없이 InnoDB 버퍼 풀 공간은 새로운 값으로, 언두 로그에는 이전 데이터가 저장된다.
UPDATE member SET m_area='경기' WHERE m_id=2;
만약 아직 커밋이 이루어지지 않은 상태에서 조회 쿼리가 날라오면, InnoDB 버퍼 풀과 언두 로그 중 어디의 값을 보여주는 것이 좋을까?
이 질문의 답은 MySQL 서버에 설정된 격리 수준(Isolation Level)에 결정된다.
이처럼 하나의 레코드에 대해 2개의 버전이 유지되어 상황에 따라 보여지는 데이터가 달라지기 때문에, 멀티 버전 동시성 제어(MVCC)라는 단어가 나오게 된것이다.
트랜잭션이 오랜 시간 동안 활성 상태인데 MySQL 서버가 느려졌다면, 이러한 일관된 읽기를 위해 언두 로그를 삭제하지 못해 발생하는 문제이다. 따라서 가능한 한 빨리 롤백이나 커밋을 하는 것을 추천한다.
데드락이란, 2개 이상의 트랜잭션이 상대방이 가진 잠금을 요구하는 상황을 의미한다.
InnoDB에서는 데드락 상황에서 일정 시간이 지나면 아예 요청을 실패하게 하고 에러 메시지를 출력하도록 하여 무한정 대기하는 상황을 방지한다.
잠금 대기 목록을 그래프 형태로 관리하면서, 데드락 감지 스레드는 트랜잭션 언두 로그의 양이 작은 순대로(롤백으로 인한 비용과 부하가 제일 적음) 강제 종료시킨다. 잠금 목록을 검사할때 역시 변경되면 안되므로 새로운 락을 걸고 데드락 스레드를 찾는다.
이때, innodb_table_locks
변수 활성화를 통해 InnoDB 스토리지 엔진이 MySQL 엔진에서 관리되는 테이블 영역의 잠금까지 볼 수 있도록 하면 더욱 정확히 데드락 상황을 감지할 수 있다.
동시 처리 스레드가 많아지거나, 각 트랜잭션이 가진 락의 개수가 많아지면 데드락 감지 스레드가 느려지게 되는데, 잠금 목록을 검사할때 거는 새로운 락으로 인해 대기 시간이 때문이다. 이에 따라 쿼리를 처리하는 스레드는 더더욱 작업을 진행할 수 없어지기 때문에 성능에 영향을 준다.
장애로 인한 데이터의 손실을 막기 위해, 기본적으로 InnoDB에서는 MySQL 서버가 시작될 때 안료되지 못한 트랜잭션이나 디스크에 일부만 기록된 데이터 페이지 등에 대한 일련의 복구 작업이 자동으로 진행된다.
일단 MySQL 서버가 기동되고 InnoDB 테이블이 인식된다면, mysqldump
를 이용해 데이터를 백업하고 해당 데이터로 MySQL 서버의 DB와 테이블을 다시 생성하자.
사진 출처
https://hyuuny.tistory.com/195
내용 출처
RealMySQL8.0
저도 개발자인데 같이 교류 많이 해봐요 ㅎㅎ! 서로 화이팅합시다!