티스토리 뷰
Transaction
질의(Query)를 하나의 묶음으로 처리해서 중간에 실행이 중단됐을 경우, 처음부터 다시 실행하는 Rollback(Noting)
을 수행하고, 오류없이 실행을 마치면 Commit(All)
을 하는 실행 단위를 의미한다.
- 다수의 사용자가 DB를 동시에 접근하도록 허용하면서 일관성을 유지하도록 보장
- 일관성을 보장하기 위해 전부 수행되던가, 하나도 수행되면 안됨(All or Nothing)
- DBMS의 트랜잭션 관리 모듈(동시성 제어 모듈, 회복 모듈로 구성) 담당
- 동시성 제어 모듈: 동시에 수행되는 여러 트랜잭션들 간의 상호 작용 제어
- 회복 모듈: DB 갱신 도중 시스템이 고장나도 DB의 일관성을 유지되도록 보장
ACID 원칙
- Transaction은 아래 네 가지 특성을 모두 만족해야 한다
원자성(Atomicity)
한 트랜잭션 내 모든 연산들이 완전히 수행되거나 전혀 수행되지 않아야 한다.
즉, 한 트랜잭션 내 모든 연산이 데이터베이스에 모두 반영되거나 전혀 반영되지 않아야 한다. 시스템이 트랜잭션 중간에 다운되면 DBMS의 회복 모듈
이 데이터베이스를 갱신한 트랜잭션의 영향을 취소시켜 트랜잭션의 원자성을 보장한다.
일관성(Consistency)
한 트랜잭션이 정확하게 수행되고 나면 데이터베이스가 하나의 일관된 상태에서 다른 일관된 상태로 바뀐다. DBMS는 CREATE TABLE
문에서 정의된 무결성 제약조건들을 유지한다. 하지만 동시에 다수 사용자가 데이터베이스에 접근하여 갱신 및 검색 연산을 수행하는 경우 무결성 제약조건만 가지고 일관성을 보장할 수 없기 때문에 동시성 제어 모듈
이 다수 사용자의 서로 상충되는 데이터베이스 접근을 조정한다.
고립성(Isolation)
한 트랜잭션이 데이터를 갱신하는 동안 다른 트랜잭션들이 접근하지 못하도록 해야한다.
트랜잭션들의 직렬 수행은 일관성을 보장하지만 성능상 적절하지 않다. 따라서 DBMS의 동시성 제어 모듈
이 트랜잭션의 고립성을 보장하기 위해 다양한 고립 수준(Isolation Level)
을 제공한다.
지속성(Durability)
한 트랜잭션이 완료되면 이 트랜잭션이 갱신한 것은 그 후 시스템에 고장이 발생하더라도 손실되지 않는다. DBMS의 회복 모듈
은 시스템이 다운되는 경우에도 트랜잭션의 지속성을 보장한다.
동시성 제어
각 트랜잭션은 데이터베이스의 일관성을 유지하므로 여러 트랜잭션들의 집합을 한 번에 한 트랜잭션씩 수행하는 직렬 스케줄(Serial Schedule)
에서는 데이터베이스의 일관성이 보장된다. 여러 트랜잭션을 동시에 수행하는 비직렬 스케줄(Non-Serial Schedule)
의 결과가 어떤 직렬 스케줄의 결과와 동일하다면 직렬가능(Serializable)
하다고 말한다.
다수 트랜잭션 병렬수행 시 발생가능한 문제
갱신 손실(Lost Update)
두개 이상의 트랜잭션이 한개의 데이터를 동시에 갱신(Update)할 때 발생,
수행 중인 트랜잭션이 갱신한 내용을 다른 트랜잭션이 덮어 씀으로써 이전 갱신이 무효가 되는 것
오손 데이터 읽기(Dirty Read)
완료되지 않은 트랜잭션(Rollback 된)이 갱신한 데이터를 읽는 것
즉, 읽기 작업을 하는 트랜잭션1
이 쓰기 작업을 하는 트랜잭션2
가 작업한 중간 데이터를 읽는 경우, 어떠한 이유로 트랜잭션2
가 작업을 롤백할 경우 트랜잭션1
은 무효가 된 데이터를 읽게 되고 잘못된 결과를 도출하게 됨
반복할 수 없는 읽기(Unrepeatable Read)
한 트랜잭션이 동일한 데이터를 여러 번 읽을 때, 서로 다른 값을 읽는 것
즉, 트랜잭션1
이 데이터를 읽고, 트랜잭션2
가 데이터를 갱신하고, 다시 트랜잭션1
이 데이터를 읽을 때 이전의 결과와 일치하지 않는 현상 발생
로킹
트랜잭션들의 동시성을 제어하기 위해 수행되는 기법으로, 동일한 데이터 항목에 대한 여러 트랜잭션들의 동시 접근을 조정하기 위해 Lock
이 사용된다. Lock
은 데이터베이스 내의 각 데이터 항목과 연관된 하나의 변수
로 Lock에 관한 정보는 Lock Table
에 유지된다.
한 데이터 항목에 대한 갱신을 위한 트랜잭션은 독점 락(X-Lock, eXclusive Lock)
을 요청하고, 읽기 목적의 트랜잭션은 공유 락(S-Lock, Shared Lock)
을 요청한다.
특정 데이터 항목에 독점 락이 걸려있는 경우
- 독점 락 요청과 공유 락 요청이 모두 락이 해제될 떄까지 대기
특정 데이터 항목에 공유 락이 걸려있는 경우
- 독점 락 요청은 대기, 공유 락 요청은 허용됨
2단계 로킹 프로토콜
락을 너무 일찍 해제하는 경우에는 락을 이용해도 동시성 제어 문제가 완전하게 해결되지 않는다. 2단계 로킹 프로토콜
에서는 락을 요청하는 것과 락을 해제하는 것이 두 개의 단계로 이루어진다.
즉, unlock
을 실행하면 그 후 lock
을 새롭게 요청할 수 없으므로, 접근해야 하는 항목 모두에 대해 미리 lock
을 요청해두어야 한다. 락을 요청하고 해제하는 연산을 포함하여 2단계 로킹 프로토콜과 관련된 모든 작업은 DBMS
에서 이루어진다.
락 확장 단계 (1단계)
트랜잭션이 데이터 항목에 대해 새로운 락을 요청할 수 있지만, 보유한 락을 하나라도 해제할 수 없다.
락 수축 단계 (2단계)
보유하고 있는 락을 해제할 수 있지만, 새로운 락을 요청할 수 없다. 락 수축 단계에서는 락을 조금씩 해제할 수도 있고, 트랜잭션 완료 시점에 한번에 모든 락을 해제할 수도 있다(일반적). Lock Point
는 한 트랜잭션에서 필요로 하는 모든 락을 걸어놓은 시점을 뜻한다.
데드락
2단계 로킹 프로토콜을 이용한 동시성 제어를 통해 다중 트랜잭션 환경에서 데이터베이스의 일관성을 유지할 수 있다. 하지만 데드락 역시 발생할 수 있는데, 서로 상대방이 보유한 락을 요청하면서 대기중인 상태일 때 발생하고, 발생 과정은 다음과 같다.
- T1이 데이터 항목 X에 대해 독점 락을 요청하여 허가받음
- T2가 데이터 항목 Y에 대해 독점 락을 요청하여 허가받음
- T1이 Y에 대해 독점 락 혹은 공유 락을 요청 -> 대기 상태에 빠짐
- T2가 X에 대해 독점 락 혹은 공유 락을 요청 -> 대기 상태에 빠짐
다중 로크 단위
성능을 위해 트랜잭션이 접근하는 튜플의 수에 따라 락을 하는 데이터 항목의 단위를 구분해야 한다. 예를 들어 많은 튜플에 접근해야 하는 응용에서 튜플 단위로만 락을 한다면 락 테이블에서 락 충돌을 검사하고, 락 정보를 기록하는 시간이 오래 걸리기 때문이다.
한 트랜잭션에서 락을 할 수 있는 데이터 항목이 두가지 이상이면 다중 로크 단위
라고 말한다.
일반적으로 락 단위가 작을수록 락에 따른 오버헤드가 증가한다. 트랜잭션이 요청한 락 단위에 대해 락을 할 때마다 락 테이블에 관련 정보를 기록하고, 트랜잭션이 끝날 때 락 테이블에서 트랜잭션에 대한 모든 락 정보를 삭제해야 하기 때문이다.
DBMS
는 각 트랜잭션에서 접근하는 튜플 수에 따라 자동적으로 락 단위를 조정한다.
트랜잭션이 요청할 수 있는 락의 단위는 다음과 같다.
- 데이터베이스 단위
- 릴레이션 단위
- 디스크 블록(페이지) 단위
- 레코드(튜플) 단위
회복
고장 유형
- 재해적 고장: 디스크가 손상을 입어 데이터베이스를 읽을 수 없는 고장
- 비재해적 고장: 재해적 고장 외의 고장
비재해적 고장 회복 알고리즘
- 로그를 기반으로 한 즉시 갱신
- 로그를 기반으로 한 지연 갱선
- 그림자 페이징
즉시 갱신
즉시 갱신에서는 트랜잭션이 데이터베이스를 갱신한 사항이 주기억 장치의 버퍼에 유지되다가 트랜잭션이 완료되기 전이라도 디스크의 데이터베이스에 기록될 수 있다.
데이터베이스의 항목에 영향을 미치는 모든 트랜잭션의 연산(갱신 연산)들에 대해 로그 레코드를 기록, 각 로그 레코드는 로그 순서 번호(LSN, Log Sequance Number)
로 식별된다.
로그 레코드 유형
[TID, start]
: 한 트랜잭션이 생성될 때 기록되는 로그 레코드[TID, X, old_value, new_value]
: 데이터 항목 X가 old_value에서 new_value로 갱신[TID, commit]
: 주어진 TID를 갖는 트랜잭션에 대한 갱신 연산이 모두 성공[TID, abort]
: 주어진 TID를 갖는 트랜잭션이 철회되었음을 나타내는 로그 레코드
회복 과정
- [TID, start], [TID, commit] 레코드가 모두 존재하는 트랜잭션들은 재수행
- [TID, start]만 존재하는 레코드는 취소
트랜잭션의 재수행(REDO)은 로그가 기록된 방향으로 진행되고, 취소(UNDO)는 로그를 역방향으로 따라가면서 진행된다.
체크포인트
DBMS는 회복시 재수행할 트랜잭션의 수를 줄이기 위해 주기적으로 체크포인트를 수행.
체크포인트를 수행하면 디스크 상에서의 로그와 데이터베이스의 내용이 일치하게 되고, 체크포인트 작업이 끝나면 로그에 [checkpoint]
로그 레코드가 기록된다.
일반적으로 체크포인트는 10~20분마다 한번씩 수행되고, 진행 과정은 다음과 같다.
- 현재 수행 중인 트랜잭션을 일시 중단
- 주기억 장치의 로그 버퍼를 디스크에 강제로 출력
- 주기억 장치의 데이터베이스 버퍼를 디스크에 강제로 출력
[checkpoint]
로그 레코드를 로그 버퍼에 기록 후 디스크에 강제 출력- 일시 중단된 트랜잭션 수행 재개
PL/SQL의 트랜잭션
오라클에서 한 트랜잭션은 암시적
으로 끝나거나, 명시적
으로 끝날 수 있다. COMMIT이나 ROLLBACK 없이 Oracle SQL Developer를 정상적으로 종료한 경우에는 트랜잭션을 암시적으로 완료하고, 비정상적으로 종료되거나 시스템에 장애가 발생한 경우 수행 중이던 트랜잭션을 암시적으로 철회한다.
또한, 사용자가 COMMIT
, ROLLBACK
, SAVEPOINT
문을 사용하여 트랜잭션의 논리를 명시적으로 제어할 수 있다.
PL/SQL
PL/SQL
은 오라클 DBMS에서 SQL 언어를 확장하기 위해 사용하는 컴퓨터 프로그래밍 언어 중 하나이다. 주로 내부에서 SQL 명령문만으로 처리하기 복잡한 트리거를 작성하는 데 쓰인다.
트랜잭션 제어
COMMIT
- 모든 락과 저장점 해제
- 모든 데이터의 변경 내용이 DB에 기록
ROLLBACK
- 현재 트랜잭션이 수행한 한 개 이상의 DML 결과를 모두 되돌리고 트랜잭션 전체 철회
SAVEPOINT
- 트랜잭션 내에 저장점을 표시하여 트랜잭션을 더 작은 부분으로 나눔
ROLLBACK TO SAVEPOINT
로 특정 SAVEPOINT 이후까지만 ROLLBACK 가능
ROLLBACK TO SAVEPOINT
- 현재 트랜잭션에서 지정된 저장점 이후에 갱신된 내용만 ROLLBACK
트랜잭션 속성
트랜잭션 속성 명시
SET TRANSACTION READ ONLY;
SELECT AVG(SALARY)
FROM EMPLOYEE
WHERE DEPT='개발부';
위와 같이 어떤 트랜잭션이 읽기 작업만 수행한다면 해당 트랜잭션이 읽기 전용임을 명시하여 DBMS가 동시성의 정도를 높일 수 있다. 만일 읽기 전용임을 명시했으면 그 트랜잭션은 어떠한 갱신 작업도 할 수 없다.
예를 들어 다음과 같은 SQL문은 허용되지 않는다.
SET TRANSACTION READ ONLY;
UPDATE EMPLOYEE
SET SALARY = SALARY * 1.06;
트랜잭션의 디폴트는 읽기
와 쓰기
를 모두 수행하는 것이다. 트랜잭션을 다음과 같이 명시하면 읽기 및 갱신 연산을 모두 수행할 수 있다.
SET TRANSACTION READ WRITE;
UPDATE EMPLOYEE
SET SALARY = SALARY * 1.06;
고립 수준
고립 수준
은 한 트랜잭션이 다른 트랜잭션과 고립되어야 하는 정도를 나타낸다. 고립 수준이 낮으면 동시성은 높아지지만 데이터의 정확성이 떨어지고, 반대로 고립 수준이 높으면 데이터는 정확해지지만 동시성이 저하되고 성능 저하가 유발될 수 있다.
응용에서 명시한 고립 수준에 따라 DBMS
가 사용하는 락 동작이 달라진다.DBMS
에서 제공하는 몇 가지 고립 수준은 다음과 같다.
READ UNCOMMITTED
가장 낮은 고립 수준
- 읽기 연산:
공유 락
걸지 않음 - 갱신 연산:
독점 락
을 걸고 트랜잭션이 끝날 때까지 보유SET TRANSACTION READ WRITE ISOLATION LEVEL READ UNCOMMITTED;
이 고립 수준에서는 오손 데이터 읽기
, 반복할 수 없는 읽기
, 팬텀 문제
가 발생할 수 있다.
READ COMMITTED
Oracle DBMS의 디폴트 고립 수준, Unrepeatable Read
발생 가능.
읽기 연산 시 공유 락
을 걸고 읽기가 끝나자마자 공유 락을 해제하고, 갱신 연산에 대해서는 독점 락
을 걸고 트랜잭션이 끝날 때까지 보유한다. 따라서 동일한 데이터를 다시 읽기 위해 공유 락을 다시 걸고 데이터를 읽으면, 이전에 읽은 값과 다른 값
을 읽는 경우가 발생할 수 있다.
SET TRANSACTION READ WRITE
ISOLATION LEVEL READ COMMITTED;
이 고립 수준에서는 반복할 수 없는 읽기
, 팬텀 문제
가 발생할 수 있다.
REPEATABLE READ
읽기 연산과 갱신 연산 각각에서 공유 락
과 독점 락
을 얻고 트랜잭션이 끝날 때까지 보유한다. 따라서 이 고립 수준에서는 반복할 수 없는 읽기
가 나타날 수 없다.
SET TRANSACTION READ WRITE
ISOLATION LEVEL REPEATABLE READ;
이 고립 수준에서는 팬텀 문제
가 발생할 수 있다.
SERIALIZABLE
가장 높은 고립 수준으로, SQL2
의 디폴트 고립 수준이다.
질의에서 검색되는 튜플들뿐만 아니라 인덱스
에 대해서도 공유 락
을 걸고 트랜잭션이 끝날 때까지 보유한다. 갱신 연산에 대해서는 독점 락
을 얻고 트랜잭션이 끝날 때까지 보유한다.
SET TRANSACTION READ WRITE
ISOLATION LEVEL SERIALIZABLE;
이 고립 수준에서는 문제가 발생하지 않는다.