-
Transaction 이란?Database/MySQL 2022. 1. 12. 09:37
Transaction 이란?
trans·ac·tion
1. 거래, 매매
2. 처리 (과정)
사전적 의미로 거래나 매매, 그리고 처리(과정) 라는 의미로 풀이되는데, 컴퓨터공학에서 표현하는 의미로는 처리(과정) 이 적합한 듯 하다.
컴퓨터공학에서 Transaction 이란, 하나로 묶은 DB 작업의 단위이다. 하나로 묶는 범위는 작업자가 임의대로 지정할 수 있다.
영화 예매를 처리하는 과정을 생각해보자.
1. 현재 상영관의 빈 자리 목록을 요청한다.
2. 좌석을 선택하여 결제한다.
3. 빈 자리 목록에 예매에 성공한 자리가 노출되지 않는다.
위 과정은 각각 DB에 서로 다른 요청을 보낸다.1번은 빈 자리 목록을 불러오는 `SELECT` 요청을 진행할 것이고, 2번과 3번은 결제 및 예매 내역에 대한 `INSERT` 혹은 `UPDATE` 명령을 수행 할 것으로 보인다.
예매를 성공하기 위해서 위 과정 중 어느 하나라도 잘못되어서는 안된다.즉, 하나의 단위로 관리되어야 한다.
이렇게 DB Command 는 여러가지로 나누어지지만 개념적으로 하나의 단위로 묶어야 하는 처리 과정을 Transaction 이라고 한다.
그렇다면, Transaction 은 어떤 특성이 있을까? ACID 라는 약자로 Transaction 이 가져야하는 특징을 설명할 수 있다.Atomicity (원자성)
위의 3가지 과정은 예매를 하기 위한 필수 과정이다. 더 이상 나눌 수 없고, 부분적으로 수행할 수 없다. 예매를 위한 최소 단위이다.
Consistency (일관성)
70개의 빈 좌석과 30개의 예매된 좌석이 있을 때, 새로운 Transaction 의 결과가 어떻게 처리되던 간에 데이터의 총 값을 일관되게 보장해야 한다. 가령 예매를 하고 난 이후 빈 좌석이 69개가 되었다면, 예매된 좌석은 31개가 되어야 한다.
Isolation (독립성)
어느 사용자의 예매가 진행 중이라면, 그 과정은 다른 사용자들의 예매 과정과 독립적이어야 한다. 다시 말해, 내가 선택한 좌석을 결제하려고 할 때 이 과정에 다른 사용자가 참여하여 가로채는 일이 없다는 것을 보장하는 성질이다.
Durability (지속성)
예매가 완료되고 난 후 그 결과가 영구적으로 지속되는 것을 의미한다. 예매 이후에 시스템 장애가 발생하더라도, 예매가 완료되었다는 사실을 확인할 수 있어야 한다.
Transaction 이 ACID 를 보장하는 방법 이해하기
Transaction 은 애플리케이션 개발을 할 때 개발자가 비즈니스 로직에 지정한 논리적인 단위 묶음이다. Transaction 을 시작하고 싶은 곳에 시작을 알리고, 종료하고 싶은 곳에 종료를 알린다.
그렇게 설정된 논리적인 단위 묶음 내에서 발생하는 데이터베이스 연산은 Transaction 이 정한 규칙에 따라 이루어지게 된다.
DB Lock
실제로 데이터의 ACID 를 보장하기 위해서는 데이터베이스가 데이터를 다루는 방식을 조정해야 한다.
Transaction 은 ACID 를 보장하기 위하여 데이터베이스가 지원하는 동시성 관리 메커니즘(lock)을 이용한다.
InnoDB 는 많은 종류의 lock 타입을 지원하고 있다. 여기서는 대표적인 lock 종류에 대하여 알아보자.
Row level locks
가장 기본적으로 InnoDB 는 2 가지 타입의 표준적인 lock 을 구현하고 있다. 첫번째는 Shared lock(s lock) 이고, 두번째는 Exclusive lock(x lock) 이다.
InnoDB 는 이 두가지 동시성 관리 메커니즘을 가지고 인덱스가 걸린 Record, 지정된 범위 내에 존재하는 Record 등에 lock 을 수행한다.Shared lock
SELECT * FROM example WHERE example.id = 1 FOR SHARE;
일반적인 SQL SELECT 문은 읽기 연산 이후에 해당 row 에 접근하는 것에 아무런 제약을 걸지 않지만, s lock 을 수행하는 명령어인 `FOR SHARE` 을 입력하면 해당 row 의 Transaction 이 종료되기 전까지 다른 Transaction 에서 수행되는 DB write 연산이 수행될 수 없다. 단, read 연산에 대해서는 제약을 걸지 않는다.
Exclusive lock
SELECT * FROM example WHERE example.id = 1 FOR UPDATE;
s lock 이 read 연산에 대해서 제약을 걸지 않는다. 반면에, x lock 을 수행하는 명령어인 `FOR UPDATE` 를 입력하면 해당 row 의 Transaction 이 종료되기 전까지 다른 Transaction 에서 수행되는 모든 DB 연산이 수행될 수 없다.
Transaction (T1) 이 데이터베이스의 한 row 에 대하여 s lock 을 걸었다고 해보자.이 때, 다른 Transaction (T2) 는 해당 row 에 대하여
- s lock 을 거는 것이 허용된다. 결과적으로 하나의 row 에 대하여 T1 과 T2 모두 s lock 을 걸 수 있다. 즉, T1 이 끝나기 전에도 해당 row 를 다른 Transaction 이 읽을 수 있다.
- x lock 을 거는 것이 허용되지 않는다. 즉, T1 이 끝나기 전에는 해당 row 를 다른 Transaction 이 수정하거나 삭제할 수 없다.
InnoDB 는 이렇게 2 가지 종류의 lock 전략으로, lock 을 거는 대상을 달리하는 여러가지 종류의 lock type 을 소개하고 있다.
공식문서에 Row level locks 와 기타 다른 lock 이 함께 소개되어 있는데, Row level locks 는 다른 lock 에서 사용될 lock 의 전략을 설명하는 것이다. Row loevel locks 를 이용하여 기타 다른 lock 전략(Intention locks, Record locks, Gap locks) 을 만든다.
Record locks
Record locks 는 index 에 거는 lock 이다. 예를 들어서, 다음과 같은 구문이 있다고 하자.
SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;
c1 컬럼이 인덱스로 지정된 컬럼일 때, c1 의 값이 10 인 인덱스 Record 에 lock 이 걸린다.
즉, c1 이 10인 Record 는 insert, update, delete 가 불가능하다.
Record locks 는 테이블에 정의된 index 가 존재하지 않더라도 수행되게 된다.InnoDB 가 primary key 를 정의하지 않은 테이블에 대해 clustered index 를 임의로 생성하기 때문이다.
Gap locks
Gap locks 는 쿼리로 검색된 index Record 들의 사이 간격에 존재하는 범위에 걸리는 lock 이다.
SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;
위 구문은 10과 20 사이의 값이 c1 컬럼에 insert 되는 것을 막는다.
Isolation Level
Transaction 은 ACID 를 보장하기 위하여 데이터베이스가 지원하는 동시성 관리 매커니즘(*lock*) 을 이용한다고 했다.
얼마나 철저히 ACID 를 보장할 것인지 혹은 어떤 방식으로 ACID 를 보장할 것인지에 대한 수준을 설정할 수가 있다.
이 것이 바로 Isolation Level 이다.
Isolation Level 을 설정하는 것은 여러 Transaction 이 동시에 변경을 수행하고 쿼리를 수행할 때 성능과 안정성, 일관성 및 결과 재현성 간의 균형을 조정하는 것이다.
Isolation Level 을 최대치로 올려서 ACID 를 강력하게 보장하겠다는 것은 곧 동시성을 위해 성능을 포기하겠다는 말이 된다.즉, 성능과 안정성 사이에는 trade-off 가 존재함을 유념하여 Isolation Level 을 설정할 필요가 있음을 미리 알아두자.
Mysql 은 SQL 표준으로 정의되어 있는 4가지 Isolation Level 을 모두 지원한다.
- READ UNCOMMITTED
- READ COMMITTED
- REPEATABLE READ
- SERIALIZABLE
위와 같은 순서로 ACID 가 보장되는 정도를 달리한다.
1순위가 가장 덜 보장하는 것이고, 4순위가 가장 엄격하게 ACID 를 보장하고 있다고 생각하면 된다.
이제 한 가지씩 세부적으로 알아보도록 하자.
READ UNCOMMITTED
READ UNCOMMITTED 는 커밋되지 않은 Record 를 읽을 수 있는 수준의 Isolation Level 이다.
여러 Transaction 사이에 존재하는 isolation 이 없는 것이므로, lock 을 사용하지 않는다.
lock 을 유지하기 위한 overhead 가 필요없기 때문에 높은 성능을 보장한다. 하지만 DIRTY READ 가 발생할 수 있음에 유념해야 한다.DIRTY READ
위의 그림에서 보듯이, Tread 1 이 데이터를 var = 50 의 데이터를 var = 100 으로 변경한 후 UPDATE 쿼리를 실행했다.
이 때, Tread 2 가 해당 Record 에 접근하면 READ UNCOMMITTED Isolation Level 에서는 Transaction 간에 Isolation 을 설정하지 않기 때문에 데이터에 접근할 수 있다.
따라서 var = 100 데이터를 가지고 올 수 있다. 그런데 여기서 Tread 1 에서 실행했던 Transaction 이 commit (종료) 되지 않고, roll back 되었다면 var 는 100 이 아니라 초기의 값인 50 으로 되돌아 간다.
Tread 2 에서는 이 사실을 알 수 없고 var = 100 기준으로 작업을 수행하게 된다. 이것이 바로 DIRTY READ 이다.READ COMMITTED
READ COMMITTED 는 커밋된 Record 를 읽을 수 있는 수준의 Isolation Level 이다.
커밋된 데이터만 읽을 수 있기 때문에, READ UNCOMMITTED 에서 발생하는 DIRTY READ 를 피할 수 있다.
READ COMMITTED 는 MySql 을 제외한 많은 RDBMS 소프트웨어에서 기본 설정으로 사용된다. MySql 은 곧 살펴볼 REPEATABLE READ 를 기본 설정으로 사용하고 있다.
다시 돌아와서, READ COMMITTED 는 각각의 SELECT 쿼리에서 커밋된 데이터만을 읽어오기 위해 해당 SELECT 쿼리가 실행되기 전 마지막 커밋된 데이터의 스냅샷을 읽어온다.
여기서 한 가지 문제점이 발생할 수 있다. 만약 하나의 Transaction 내에서 여러개의 SELECT 쿼리가 실행되었다고 해보자. 그렇다면 실행된 각각의 SELECT 쿼리는 쿼리가 실행되기 전 마지막 커밋된 데이터의 스냅샷을 읽어오기 때문에, 다른 결과를 보여줄 수 있다.이러한 현상을 NON REPEATABLE READS 라고 한다.
REPEATABLE READ
REPEATABLE READ 는 InnoDB 의 기본 Isolation level 이다.
Transaction 내에 첫번째 SELECT 쿼리에 대한 Snapshot 결과를 반복적으로 사용하여 해당 Transaction 내에서는 동일한 결과를 읽어올 수 있다.
Transaction 이 실행되고 있는 동안에는 다른 Transaction 에 의한 UPDATE, DELETE 쿼리의 결과가 Commit 되더라도 수행되고 있는 Transaction 에는 그 결과가 반영되지 않는다.이와 같이 일관된 읽기를 보장하기 위해서 Snapshot 을 유지해야 하기 때문에 추가적인 overhead 가 다소 발생할 수 있다.
REPEATABLE READ 는 Row level locks (FOR UPDATE, FOR SHARE) 를 사용한 READ 연산과 UPDATE, DELETE 문에 대해서 lock 을 걸게 된다.lock 을 거는 방식은 SELECT 문에서 사용한 검색 조건에 따라 달라진다.
- 단일한 검색 조건을 사용한 검색의 경우, InnoDB 는 오직 검색된 index record 에 대해서만 lock 을 건다. 이 경우 테이블에 대한 INSERT 자체를 막는 것은 아니기 때문에 side-effect (PHANTOM READ) 가 발생할 수 있다.
- 다른 검색 조건에 대해서는, InnoDB 는 검색된 인덱스 범위에 gap locks 혹은 next-key locks 를 사용하여 lock 을 건다. 해당 범위 내에 INSERT 가 발생하는 것을 막는다.
위에서 잠시 언급했지만 REPEATABLE READ 는 단일 검색조건의 경우 INSERT 문에 대한 lock 을 걸지 않기 때문에 PHANTOM READ 라는 문제점을 일으킬 가능성이 있다.
PHANTOM READTransaction 1 이 단일한 검색 조건을 사용하였고, 그에 대한 결과가 다른 Transaction 내에서 실행되는 INSERT 에 의해서 결과가 달라지는 경우를 PHANTOM READ 라고 한다.
SERIALIZABLE
SERIALIZABLE 은 REPEATABLE READ 와 유사하다.
단, autocommit 조건이 비활성화된 경우에 암묵적으로 모든 SELECT 문에 FOR SHARE 를 수행한다.
만일 autocommit 이 활성화되어 있다면, SELECT 문은 그 자체가 Transaction 이 된다. 모든 SELECT 문에 FOR SHARE 를 강제함으로써 다른 Transaction 에 의한 변경 및 추가를 막는다.
가장 엄격한 Transaction isolation level 이며, 그만큼 데이터 동시 처리 능력이 떨어진다.
Transaction 이라는 것이 데이터베이스 시스템에서 매우 중요한 부분이고, 데이터 저장의 전제조건과도 같은 ACID 원칙을 다루고 있다보니 학습해야 하는 양이 매우 크다.
잘 정리해서 기억해두자.
반응형