일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- UXUI챌린지
- 백엔드 부트캠프
- baekjoon
- 디자인강의
- 패스트캠퍼스
- UXUIPrimary
- UXUI기초정복
- 디자인챌린지
- 오픈패스
- 국비지원
- 부트캠프
- 백엔드
- Java
- mysql
- 환급챌린지
- KDT
- 국비지원교육
- 내일배움캠프
- 백준
- 내일배움카드
- 오블완
- 객체지향
- Be
- Spring
- 티스토리챌린지
- 디자인교육
- 국비지원취업
- 백엔드개발자
- 오픈챌린지
- OPENPATH
- Today
- Total
군만두의 IT 공부 일지
[스터디] 07. MVCC와 Non-Locking Consistent Read 본문
목차
MVCC와 Non-Locking Consistent Read는 InnoDB의 주요 기능 중 하나로, 동시성 제어와 성능 최적화 측면에서 중요합니다. 이를 실습해 보면서, 언두 로그(Undo Log)의 역할, 트랜잭션 격리 수준의 영향, 그리고 어떻게 MVCC가 일관된 읽기를 보장하는지 등을 다루려고 합니다.
1. MVCC(Multi-Version Concurrency Control)
- MVCC는 여러 트랜잭션이 동시에 수행될 때, 각 트랜잭션이 자신만의 데이터 버전을 사용하는 방식임.
- 이를 통해 다른 트랜잭션의 잠금 대기 없이 데이터 읽기가 가능함.
- InnoDB는 언두 로그(Undo Log)를 사용해 MVCC를 구현하며, 변경되기 전의 데이터(각 레코드의 이전 버전)를 보관함.
1-1. MVCC의 특징
- 일관된 읽기: SELECT 쿼리는 다른 트랜잭션이 커밋되지 않았더라도 기존 데이터를 읽을 수 있음.
- 격리 수준: READ COMMITTED, REPEATABLE READ 수준에서 MVCC가 작동함.
- 언두 로그: 데이터 변경 전 상태를 보관해 필요시 과거 데이터를 조회할 수 있음.
* 격리 수준: 데이터베이스에서 트랜잭션이 서로 영향을 주지 않도록 관리하는 방법
2. 잠금 없는 일관된 읽기(Non-Locking Consistent Read)
- Non-Locking Consistent Read는 트랜잭션이 커밋되지 않아도 다른 트랜잭션이 데이터를 읽을 수 있게 해주는 기능을 제공함.
- 트랜잭션 격리 수준에 따라 동작이 다름. 책에서는 아래 수준이면 INSERT와 연결되지 않은 순수 읽기 작업은 항상 바로 잠금 없이 실행된다고 함.
- READ UNCOMMITTED: 잠금을 사용하지 않으며 변경 중인 데이터도 읽을 수 있음.
- READ COMMITTED: 커밋된 데이터만 읽음.
- REPEATABLE READ: 트랜잭션이 시작된 시점의 데이터만 일관되게 읽음.
3. 실습
3-1. 환경설정
먼저 MySQL에서 InnoDB 테이블을 생성하고 데이터를 삽입(INSERT)합니다. 코드는 책에 있는 것을 사용하려고 합니다.
CREATE TABLE member (
m_id INT AUTO_INCREMENT PRIMARY KEY,
m_name VARCHAR(20) NOT NULL,
m_area VARCHAR(100) NOT NULL,
INDEX ix_area (m_area)
);
INSERT INTO member (m_id, m_name, m_area) VALUES (12, '홍길동', '서울');
INSERT INTO member (m_id, m_name, m_area) VALUES (13, '이몽룡', '전주');
COMMIT;
3-2. 트랜잭션 설정 및 실행
서로 다른 트랜잭션에서 데이터를 변경(UPDATE)하고 읽는 상황을 통해 MVCC와 Non-Locking Consistent Read를 실습합니다.
1) 세션 1: 트랜잭션 시작 및 데이터 변경
START TRANSACTION;
UPDATE member SET m_area = '경기' WHERE m_id=12;
2) 세션 2: 트랜잭션 시작 및 데이터 읽기
START TRANSACTION;
SELECT * FROM member WHERE m_id=12;
-- 커밋되지 않았으므로, 기존의 m_area 값 '서울'을 읽음
3) 세션 1: 트랜잭션 커밋
COMMIT;
4) 세션 2: 데이터 재조회
SELECT * FROM member WHERE m_id=12;
-- 커밋된 m_area 값 '경기'을 읽음
언두 로그가 변경되기 전의 데이터를 유지해 다른 트랜잭션이 데이터를 일관되게 읽을 수 있도록 한다는 것을 확인했습니다. 언두 로그는 데이터의 이전 버전을 보관하며, 이를 통해 MVCC가 구현됩니다.
3-3. 데드락 감지
MVCC가 동작하는 상황에서도 데드락이 발생할 수 있습니다. InnoDB는 데드락 감지 스레드를 통해 자동으로 데드락을 감지하고 해결합니다.
--- Safe Update Mode를 일시적으로 끄기
SET SQL_SAFE_UPDATES = 0;
-- 세션 1
START TRANSACTION;
UPDATE member SET m_area = '대구' WHERE m_name = '홍길동';
-- 세션 2
START TRANSACTION;
UPDATE member SET m_area = '광주' WHERE m_name = '이몽룡';
-- 세션 1: 데이터 업데이트 (세션 2가 잠금 중)
UPDATE member SET m_area = '광주' WHERE m_name = '이몽룡';
-- 세션 2: 데이터 업데이트 (세션 1이 잠금 중)
UPDATE member SET m_area = '대구' WHERE m_name = '홍길동';
--- Safe Update Mode 활성화
SET SQL_SAFE_UPDATES = 1;
사진은 첨부하지 못했으나, 데드락 감지 시 세션 2 트랜잭션이 롤백되고 세션 1 트랜잭션은 정상 진행되는 것을 확인할 수 있습니다.
3-4. 트랜잭션 격리 수준별 MVCC 동작 방식
다양한 트랜잭션 격리 수준에서 MVCC가 어떻게 다르게 동작하는지 실습합니다.
1) 격리 수준 설정
MySQL은 기본적으로 REPEATABLE READ 격리 수준을 사용하지만, 실습을 위해 다른 격리 수준으로 변경하며 MVCC의 차이를 확인합니다.
1-1) READ COMMITTED 격리 수준 (위 실습과 동일함)
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
-- 세션 1: 데이터 변경
UPDATE member SET m_area = '부산' WHERE m_name = '홍길동';
-- 세션 2: 데이터 읽기
SELECT * FROM member WHERE m_name = '홍길동';
-- 커밋되지 않았으므로 '서울'을 읽음
-- 세션 1: 트랜잭션 커밋
COMMIT;
-- 세션 2: 데이터 재조회
SELECT * FROM member WHERE m_name = '홍길동';
-- '부산' 값을 읽음
READ COMMITTED는 커밋된 데이터만을 읽게 되므로, 세션 1이 커밋되기 전까지 세션 2는 변경된 값을 읽지 못합니다.
1-2) REPEATABLE READ 격리 수준
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
-- 세션 1: 데이터 변경
UPDATE member SET m_area = '인천' WHERE m_name = '홍길동';
-- 세션 2: 데이터 읽기
SELECT * FROM member WHERE m_name = '홍길동';
-- 트랜잭션 시작 시점의 '부산'을 읽음
REPEATABLE READ 격리 수준에서는 트랜잭션이 시작된 시점의 데이터를 계속 읽게 됩니다.
1-3) READ COMMITTED 격리 수준
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
-- 세션 1: 데이터 변경
UPDATE member SET m_area = '대전' WHERE m_name = '홍길동';
-- 세션 2: 데이터 읽기
SELECT * FROM member WHERE m_name = '홍길동';
-- 커밋되지 않은 변경 중인 데이터 '대전'을 읽음
-- 세션 1: 트랜잭션 롤백
ROLLBACK;
-- 세션 2: 데이터 재조회
SELECT * FROM member WHERE m_name = '홍길동';
-- 롤백된 후 '부산'을 읽음
READ UNCOMMITTED에서는 트랜잭션이 커밋되지 않았더라도 변경된 데이터를 읽을 수 있으며, 이로 인해 일관성 문제나 더러운 읽기가 발생할 수 있습니다.
2. 언두 로그와 리두 로그
이번에는 언두 로그와 리두 로그가 어떻게 동작하는지 살펴보겠습니다. 언두 로그는 트랜잭션 롤백 시 데이터 복구에 활용되며, 리두 로그는 시스템 장애 발생 시 커밋된 트랜잭션을 복구하는 데 사용됩니다.
1) 언두 로그를 통한 트랜잭션 롤백
언두 로그는 트랜잭션이 롤백될 때, 데이터가 변경되기 전 상태로 되돌리는 역할을 합니다.
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
-- 세션 1: 데이터 변경
UPDATE member SET m_area = '대전' WHERE m_name = '홍길동';
-- 세션 2: 데이터 읽기
SELECT * FROM member WHERE m_name = '홍길동';
-- 커밋되지 않은 변경 중인 데이터 '대전'을 읽음
-- 세션 1: 트랜잭션 롤백
ROLLBACK;
-- 세션 2: 데이터 재조회
SELECT * FROM member WHERE m_name = '홍길동';
-- 롤백된 후 '부산'을 읽음
언두 로그가 트랜잭션 롤백 시 데이터의 변경 사항을 원래 상태로 복구하는 데 사용되는 것을 확인할 수 있습니다.
2) 리두 로그를 통한 데이터 복구
리두 로그는 커밋된 트랜잭션이 디스크에 기록되지 않은 상태에서 시스템 장애가 발생했을 때, 이를 복구하는 역할을 합니다. 직접적인 실습은 어렵기 때문에 시뮬레이션만 설명합니다.
START TRANSACTION;
UPDATE member SET m_area = '광주' WHERE m_name = '이몽룡';
COMMIT;
-- System Crash 시 리두 로그를 통해 커밋된 데이터 '광주'가 복구됨
3. MVCC를 활용한 동시성 제어
마지막으로 MVCC의 성능 최적화를 위한 실습을 진행합니다. MVCC는 다수의 트랜잭션이 동시에 데이터에 접근하는 상황에서도 성능을 최적화하는 데 중요한 역할을 합니다. 여기서는 다수의 SELECT 쿼리가 동시에 실행될 때 MVCC가 얼마나 효율적으로 작동하는지 확인해보겠습니다.
1) 대량의 데이터 삽입
CREATE TABLE numbers_table (
id INT PRIMARY KEY
);
-- 1부터 1000까지 숫자 삽입
INSERT INTO numbers_table (id)
SELECT ROW_NUMBER() OVER () AS id
FROM information_schema.columns
LIMIT 1000;
-- member 테이블에 대량 데이터 삽입
INSERT INTO member (m_name, m_area)
SELECT CONCAT('User', id), CASE
WHEN id % 3 = 0 THEN '서울'
WHEN id % 3 = 1 THEN '부산'
ELSE '대구'
END
FROM numbers_table;
2) 동시 SELECT 쿼리 실행
여러 세션에서 동시에 SELECT 쿼리를 실행하여 MVCC가 여러 트랜잭션 간의 충돌을 어떻게 방지하고, 성능을 어떻게 최적화하는지 확인합니다.
-- 세션 1: 트랜잭션 시작 및 데이터 읽기
START TRANSACTION;
SELECT * FROM member WHERE m_area = '서울';
-- 세션 2: 트랜잭션 시작 및 데이터 읽기
START TRANSACTION;
SELECT * FROM member WHERE m_area = '부산';
이 실습에서는 두 세션에서 동시에 SELECT 쿼리를 실행하지만, MVCC 덕분에 두 세션이 서로의 작업에 방해받지 않고 데이터를 읽을 수 있습니다. 언두 로그 덕분에 변경 전 데이터를 참조할 수 있기 때문입니다.
MVCC, Non-Locking Consistent Read, 언두 로그, 리두 로그, 트랜잭션 격리 수준 및 데드락 감지 등 InnoDB의 핵심 기능을 살펴보고 실습해 보았습니다. 막연하게 이론으로만 공부했을 때는 이해하기 어려웠는데, 실습을 통해 어떻게 동작하는지 이해할 수 있었습니다.
이 글은 『Real MySQL 8.0 (1권)』 책을 학습한 내용을 정리한 것입니다.
'학습일지 > 데이터베이스' 카테고리의 다른 글
[스터디] 09. 트랜잭션과 잠금 (0) | 2024.09.05 |
---|---|
[스터디] 08. MyISAM 스토리지 엔진 아키텍처 및 MySQL 로그 파일 (0) | 2024.08.30 |
[스터디] 06. InnoDB 스토리지 엔진 아키텍처 (0) | 2024.08.15 |
[스터디] 05. InnoDB vs MyISAM (0) | 2024.08.08 |
[스터디] 04. 아키텍처 (1) | 2024.08.01 |