MySQL의 격리 수준 종류와 특징

Ilyoung Hwang·2023년 3월 6일
0

개요

트랜잭션의 격리 수준(Isolation Level)이란 여러 트랜잭션이 동시에 처리될 때 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있게 허용할지 말지를 결정하는 것이다.

격리 수준은 크게 READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE 의 4가지로 나뉜다.

1. READ UNCOMMITTED

READ UNCOMMITTED 격리에서는 각각 트랜잭션에서 변경 내용이 COMMIT 이나 ROLLBACK 여부에 상관없이 다른 트랜잭션에서 조회할수 있다.

예시는 아래와 같다.

  1. 사용자 A는 users id 가 1 이고 first_name 이 "Hong" 인 사원을 INSERT
START TRANSACTION;
INSERT INTO users VALUES(1, 'Hong');
  1. 사용자 B는 사용자 A가 COMMIT 하기도전에 id 가 1이고 first_name 이 "Hong" 인 사원을 검색할 수 있다.
SELECT * FROM users WHERE id = 1 AND first_name = 'Hong';
  1. 사용자 A는 작업을 COMMIT 한다.
COMMIT;

이처럼 하나의 트랜잭션에서 COMMIT 되지도 않았는데 다른 트랜잭션에서 조회할 수 있는 문제를 더티리드(Dirty Read) 라고 한다.

2. READ COMMITTED

READ COMMITTED는 오라클에서 기본으로 사용되는 격리 수준이며, 위에서 발생한 더티리드(Dirty Read)는 발생하지 않는다. COMMIT 완료된 데이터만 조회할 수있기 때문이다.

예시를 한번 보자.

  1. 사용자 B가 first_name 이 "Hong" 인 사람을 검색
START TRANSACTION;
SELECT * FROM users WHER first_name = 'Hong';
  1. 사용자 A가 id가 1번인 first_name 을 "Hong" 에서 "Hwang" 로 변경
START TRANSACTION;
UPDATE users SET first_name = 'Hwang';
COMMIT;
  1. 사용자 A가 COMMIT 하기 전에 사용자 B가 id 1번인 users 를 검색하게 되면 "Hong" 로 조회된다. 언두영역에 저장된 데이터 조회

그 이유는 커밋되기 전까지는 변경된 내역을 조회할 수 없기 때문이다.

이 격리수준에서는 NON-REPEATABLE READ 이라는 부정합의 문제가 발생을 한다. 이 문제는 하나의 트랜잭션내에서 똑같은 SELECT 쿼리를 실행 했을때 항상같은 결과를 조회해야되는데, 조회할 때마다 결과가 달라진다.

3. REPEATABLE READ

REPEATABLE READ는 MySQL의 InnoDB 에서 기본적으로 사용한다.
이 격리 수준에서는 동일한 트랜잭션 내에서는 동일한 쿼리 결과를 보장한다.

한 트랜잭션에서 데이터를 읽어오면 해당 데이터는 현재 트랜잭션이 끝날 때까지 다른 트랜잭션에 의해 수정되지 않는다. 따라서 위에서 발생한 NON-REPEATABLE READ 부정합이 발생하진 않는다.

그 이유는 트랜잭션이 RollBack 이 될 가능성에 대비해 변경되기 전 레코드를 언두 영역에 저장해두기 때문에 일관성 있는 데이터를 유지할 수있다.

언두영역에 저장된 모든 레코드에는 트랜잭션 번호가 있다. 레코드들은 스토리지 엔진으로 부터 필요없다고 생각되면 삭제를 하게 된다.

자... 해당 격리 수준이 작동하는 방식은 다음과 같다.

  1. A의 트랜잭션번호는 12, B의 트랜잭션 번호는 10이라고 치자.
START TRANSACTION;
SELECT * FROM users WHERE = 1;
  1. A의 트랜잭션에서 users 의 id가 1인 first_name 을 "Toto" 로 변경하고 커밋한다. "Hong" 값은 언두 영역으로 백업이 된다.
START TRANSACTION;
UPDATE users SET first_name = 'Toto' WHERE id = 1;
COMMIT;
  1. B의 트랜잭션에서 id가 1인 사원을 A 트랜잭션 변경 전,후 로 조회를 한다고 하면 항상 결과는 언두영역의 변경전의 first_name 을 가져올것이다.
SELECT * FROM users WHERE id = 1;

그리고 REPEATABLE READ 에서도 부정합이 발생할 수 있는데 그것을 팬텀리드(Phantom Read) 라고 한다. 아래의 예시에서 확인해보자.

  1. 사용자 B가 id가 1번 이상인 사람을 조회한다. 트랜잭션(T-ID:10)시작
START TRANSACTION; 
SELECT * FROM users WHERE id >= 1 FOR UPDATE;
  1. 동시에 사용자 A가 id가 2번인 사람을 저장한다. 트랜잭션(T-ID:20)시작 및 커밋
START TRANSACTION;
INSERT INTO users VALUES (2, 'Hwang');
COMMIT;
  1. 사용자 B가 1번인상인 사람을 다시 조회한다.
SELECT * FROM WHERE id >= 1 FOR UPDATE;

위의 3번의 결과는 1번의 결과와 같아야하지만 다르다. 이 현상을 위에서 말한 팬텀리드(Phantom Read)라고 한다.

이 현상은 갭 락과 넥스트 키락으로 REPEATABLE READ 격리 수준에서는 발생하지않으니 격리성을 한단계 더 높일 필요는 없다.

4. SERIALIZABLE

SERIALIZABLE이 설정되면 트랜잭션에서 읽기작업시 공유잠금을 무조건 획득해야 되며 다른 트랜잭션은 해당 레코드에 접근할 수 없게된다.
보통 SERIALIZABLE 설정까지 하진 않는다.

참고


http://www.yes24.com/Product/Goods/103415627

0개의 댓글