개발일기

(2) MySQL 아키텍처 본문

데이터베이스/MySQL

(2) MySQL 아키텍처

한둥둥 2025. 3. 15. 11:29

MVCC (Multi Version Concurrency Control)

일반적으로 레코드 레벨의 트랜잭션을 지원하는 DBMS  제공하는 기능이며, MVCC 가장 큰 목적인 잠금을 사용하지 않는 일관된 읽기를 제공하는 데 있다. 

InnoDB 언두 로드(Undo log)를 이용해 이 기능을 구현한다. 

멀티 버전이라 함은 하나의 레코드에 대해 여러 개의 버전이 동시에 관리된다는 의미다. 이를 위해 격리 수준(Isolation Level) 이 READ_COMMITED 인 MySQL 서버에서 InnoDB 스토리지 엔진을 사용하는 테이블의 데이터 변경

 

 

INSERT 문 실행시 아래 처럼 바뀜 

 

 

update 후 

 

UPDATE 문장이 실행되면 커밋 실행 여부와 관계없이 InnoDB 버퍼 풀은 새로운 값인 '경기'로 업데이트된다. 그리고 디스크 데이터 파일에는 체크포인트나 InnoDB의 Write 스레드에 의해 새로운 값으로 업데이트돼 있을 수 있고 아닐 수도 있다. (ACID 보장하기 때문에 일반적으로는 InnoDB 버퍼 풀과 데이터 파일은 동일한 상태라고 가정해도 무방하다) 

아직 COMMIT 이나 ROLLBACK 이 되지 않은 상태에서 다른 사용자가 다음 같은 쿼리로 작업 중인 레코드를 조회하면 어디에 있는 데이터를 조회할까?

 

SELECT * FROM member WHERE m_id=12;

 

MySQL 서버 시스템 변수(transaction_isolation) 설정된 격리 수준(Isolation level)에 따라 다르다. 

격리 수준이 READ_UNCOMMITED 경우에는 InnoDB 버퍼 풀이 현재 가지고 있는 변경된 데이터를 읽어서 반환한다. 

즉, 데이터가 커밋됐든 아니든 변경된 상태의 데이터를 반환한다. 그렇지 않고 READ_COMMITED나 그 이상의 격리 수준(REPEATABLE_READ, SERIALIZABLE) 인 경우에는 아직 커밋되지 않았기 떄문에 InnoDB 버퍼 풀이나 데이터 파일에 있는 내용 대신 변경되기 이전의 내용을 보관하고 있는 언두 영역에 데이터를 보관한다. 

이러한 과정을 데이터베이스에서 MVCC 라고 부르며 하나의 레코드에 대해 2개 버전이 유지되고, 필요에따라 데이터가 보여지는 여러가지 상황에따라 달라지는 구조다. 

 

 

잠금 없는 일관된 읽기(Non-Locking Consistent Read)

InnoDB 스토리지 엔진은 MVCC 기술을 이용해 잠금을 걸지 않고 읽기 작업을 수행한다. 잠금을 걸지 않기 때문에 InnoDB에서 읽기 작업은 다른 트랜잭션이 가지고 있는 잠금을 기다리지 않고, 읽기 작업이 가능하다. 

 

격리 수준이 SERIALIZABLE이 아닌 READ_UNCOMMITED나 READ_COMMITED, REPEATABLE_READ 수준인 경우 INSERT와 연결되지 않은 순수한 읽기(SELECT) 작업은 다른 트랜잭션의 변경 작업과 관계없이 항상 잠금을 대기하지 않고 바로 실행된다. 

특정 사용자가 레코드를 변경하고 아직 커밋을 수행하지 않았다 하더라도 이 변경 트랜잭션이 다른 사용자의 SELECT 작업을 방해하지 않는다. 이를 '잠금 없은 일관된 일기'라고 표현한다.  InnoDB는 언두로그를 사용해 변경되 전의 데이터를 읽는다. 

 

 

자동 데드락 감지 

스토리지 엔진은 내부적으로 잠금이 교착 상태에 빠지지 않았는지 체크하기 위해 잠금 대기 목록을 그래프(Wait-for List) 형태로 관리한다. InnoDB 스토리지 엔진은 데드락 감지 스레드를 가지고 있어서 데드락 감지 스레드가 주기적으로 잠금 대기 그래프를 검사해 교착 상태에 빠진 트랜잭션들을 찾아서 종료한다. 

어느 트랜잭션을 먼저 강제 종료할 것인지를 판단하는 기준은 트랜잭션의 언두 로그 양이며, 언두 로그 레코드를 더 적게 가진 트랜잭션이 일반적으로 롤백의 대상이 된다. 트랜잭션이 언두 레코드를 적게 가졌다는 이야기는 롤백을 해도 언두 처리를 해야 할 내용이 적다는 이야기다. 

 

InnoDB 스토리지 엔진은 상위 레이어인 MySQL 엔진에서 관리되는 테이블 감즘(LOCK TABLE 명령으로 잠긴 테이블)은 볼 수가 없어서 데드락 감지가 불확실할 수도 있는데, innodb_table_locks 시스템 변수를 활성화하면 InnoDB 스토리지 엔진 내부의 레코드 잠금뿐만 아니라 테이블 레벨의 잠금까지 감지할 수 있게 된다. 특별한 이유 없으면 innodb_talbe_locks 변수를 활성화 하자 

 

동시 처리 스레드가 많아지거나 각 트랜잭션이 가진 잠금의 개수가 많아지면 데드락 감지 스레드가 느려진다. 데드락 감지 스레드는 잠금 목록을 검사해야 하기 때문에 잠금 상태가 변경되지 않도록 잠금 목록이 저장된 리스트에 새로운 잠금을 걸고 데드락 스레드를 찾게 된다. 

데드락 스레드가 느려지면 서비스 쿼리 처리 중인 스레드는 더는 작업을 진행하지 못하고 대기하면서 서비스에 악영향을 미친다. 

 

위에 문제를 해결하기 위해 MySQL 서버는 innodb_deadlock-detect 시스템 변수를 제공하며, iindob_deadlock_detect를 OFF로 설정하면 더이상 데드락 감지 스레드 작동하지 않는다. 

 

이러면 스토리지 엔진 내부에서 2개 이상의 트랜잭션이 상대방이 가진 잠금을 요구하면 무한정대기한다. 

'데이터베이스 > MySQL' 카테고리의 다른 글

MYSQL 아키텍처  (0) 2025.03.01