nathan_H
[DataBase] 트랜잭션 병행 제어를 위한 락킹 본문
이번 포스팅은 지난 트랜잭션 포스팅의 연장선으로 트랜잭션이 어떤 방식으로 관리되는지에 대한 내용을 담고자 한다.
병행 수행과 병행 제어
- DBMS는 여러 사용자가 데이터베이스를 동시에 공유할 수 있도록 여러 개의 트랜잭션이 동시에 수행되는 병행 수행을 지원을 하기 되는데, 병행 수행은 실제로 여러 트랜잭션들이 차례로 번갈아 수행되는 인터리빙 방식을 진행함.
- 병행 수행을 하더라도 각 트랜잭션이 다른 트랜잭션의 방해를 받지 않고 정확한 수행 결과를 얻을 수 있도록 제어해야 함.
- 서로 다른 데이터를 사용하여 연산을 수행하는 경우에는 괜찮지만, 같은 데이터에 접근하여 변경하려고 할 때 문제가 발생함.
- 여러 개의 트랜잭션이 병행 수행하면서 같은 데이터 접근하여 연산을 실행하더라도, 문제가 발생하지 않고 정확한 수행 결과를 얻을 수 있도록 트랜잭션의 수행을 제어하는 것을 병행 제어 또는 동시성 제어라고 함.
병행 수행의 문제
실제 디비의 병행 수행이 이루어질 때, 크게 3가지 문제가 발생할 수 있다. 그래서 각 문제가 무엇이고 어떻게 해결하는지 알아보자.
Lost Update (갱신 분실)
- 하나의 트랜잭션이 수행한 데이터 변경 연산의 결과를 다른 트랜잭션이 덮어 변경 연산이 무효화되는 것.
- 두 개의 트랜션이 한 개의 데이터를 동시에 갱신할 때 발생하는 문제로 데이터베이스 상에서 절대 발생하면 안 되는 문제이다.
위 그림 설명
Alice의 변경 연산의 결과가 Bob의 연산에 의해 무효화됨.
Inconsistency (모순성)
모순성은 하나의 트랜잭션이 여러 개의 데이터 변경 연산을 실행할 때 일관성 없는 상태의 데이터베이스에서 데이터를 가져와 연산을 실행함으로써 모순된 결과가 발생하는 것.
위 그림 설명
- T1이 X 조회 (x = 10)
- T2도 X 조회 (x = 10)
- T1이 X의 값을 갱신 (10 → 15)
- T2가 다시 X 값을 조회 시 변경된 15가 조회됨.(원래는 X의 값이 조회 시 10이 나오기를 기대했으나, T1에서 변경 연산이 수행이 되어 모순된 결과가 나옴)
Cascading rollback(연쇄 복귀)
- 트랜잭션이 완료되기 전에 장애가 발생하여 rollback 연산을 수행하면, 이 트랜잭션이 장애 발생 전에 변경한 데이터를 가져가 변경 연산을 실행한 또 다른 트랜잭션에도 rollback 연산을 연쇄적으로 실행해야 함.
- 장애가 발생한 트랜잭션이 rollback 연산을 실행하기 전에, 변경한 데이터 가져가 사용하는 다른 트랜잭션이 수행을 완료해버리면 rollback 연산을 실행할 수 없어 큰 문제가 발생하게 됨.
위 그림 설명
- T2의 연산의 경우 T1 연산에 의존적이고 연쇄적으로 T3 → T2, T4 → T3에 의존적.
- 여기서 T1에서 문제가 발생에 Rollback 연산 실행 시 연쇄적으로 T2, T3, T4 연산에 대해서도 Rollback이 실행돼야 함
Transcation Schedule : 트랜잭션 스케줄
- 데이터베이스의 병행 수행에는 트랜잭션들이 차례로 번갈아 가면서 수행되는 인터리빙 방식으로 진행됨
- 트랜잭션에 있는 연산을 실행하는 순서에 따라 트랜잭션들의 수행 결과가 달라지기도 하고, 병행 수행에 따른 문제가 발생하기도 한다. 그러므로 여러 트랜잭션을 병행 수행할 때는 트랜잭션들의 연산을 실행하는 순서가 중요.
트랜잭션 스케줄은 크게 3가지로 구성
- 직렬(Serial) 스케줄 : 인터리빙 방식을 이용하지 않고 각 트랜잭션 별로 연산들을 순차적으로 실행시키는 것.
- 비직렬(Nonserial) 스케줄 : 인터리빙 방식을 이용하여 트랜잭션들 병행해서 수행하는 것.
- 직렬 가능(Serializable) 스케줄 : 직렬 스케줄과 정확한 결과를 생성하는 비직렬 스케줄
직렬 스케줄 (Serial)
- 인터리빙 방식을 이용하지 않고 각 트랜잭션 별로 연산들을 순차적으로 실행시키는 것.
- 직렬 스케줄에 따라 수행하면, 모든 트랜잭션이 완료될 때까지 다른 트랜잭션의 방해를 받지 않고 독립적으로 수행.
- 독립적으로 실행하기 때문에, 트랜잭션들이 동시에 수행되는 병행 수행이라고 할 수 없음
위 예시의 경우 T1의 연산이 모두 실행된 후 T2의 연산이 수행됨.
비직렬 스케줄 (nonserial)
- 인터리빙 방식을 이용하고 트랜잭션을 병행해서 수행시키는 것.
- 비직렬 스케줄은 트랜잭션이 돌아가면서 연산들을 실행하기 때문에 하나의 트랜잭션이 완료되기 전에 다른 트랜잭션이 연산이 실행될 수 있음.
- 비직렬 스케줄에 따라 여러 트랜잭션을 병행 수행하면서 갱신 분실, 모순성, 연쇄 복귀 등의 문제가 발생할 수 있어 최종 수행 결과의 정확성을 보장할 수 없음.
직렬 가능 스케줄 (serializable)
- 직렬 스케줄에 따라 수행한 것과 같이 정확한 결과를 생성하는 비직렬 스케줄.
- 모든 비직렬 스케줄이 직렬 가능한 것은 아님.
- 수행 결과가 동일한 직렬 스케줄이 없는 것들은 결과의 정확성을 보장할 수 없으므로 직렬 가능 스케줄이 아니다.
- 직렬 가능 스케줄은 직렬 스케줄 과는 다르다. 직렬 스케줄은 정확은 결과를 얻을 수 있지만, 인터리빙 방식을 이용하지 않고 트랜잭션들이 독립적으로 수행되므로 병행 수행이 아니다. 반면, 직렬 가능 스케줄은 인터리빙 방식을 이용하여 여러 트랜잭션을 병행 수행하면서도, 정확한 결과를 얻을 수 있다. 직렬 가능 스케줄을 이용해 트랜잭션을 병행 수행해야 한지만 직렬 가능 스케줄인지 판단하는 일은 쉽지 않다.
- 직렬 가능 스케줄인지를 검사하기보다는 직렬 가능성으로 보장하는 병행 제어 기법을 사용.
병행 제어 기법
여러 트랜잭션을 병행 수행하면서도 정확한 결과를 얻을 수 있는 직렬 가능성을 보장받기 위해 사용함
병행 제어 기법의 기본 원리는 모든 트랜잭션이 따르면 직렬 가능성이 보장되는 나름의 규약을 정의하고, 트랜잭션들이 이 규약을 따르도록 하는 것.
그러므로 트랜잭션 스케줄이 직렬 가능 스케줄인지를 미리 검사할 필요가 없음.
락킹 기법의 개념
- 락킹 기법은 병행 수행되는 트랜잭션들이 동일한 데이터에 동시에 접근하지 못하도록 lock과 unlock이라는 두 개의 연산을 이용해 제어.
- Lock는 데이터베이스 내의 각 데이터 항목과 연관된 하나의 변수
- 락킹 기법의 기본 원리는 한 트랜잭션이 먼저 접근한 데이터에 대한 연산을 모두 마칠 때까지, 해당 데이터에 다른 트랜잭션이 접근하지 못하도록 상호 배제하여 직렬 가능성을 보장하는 것.
- 락킹 기법에서 lock 연산은 트랜잭션이 사용할 데이터에 대한 독점권을 가지기 위해 사용함.
- unlock 연산은 트랜잭션이 데이터에 대한 독점권을 반납하기 위해 사용함.
lock 종류
공용(shared) Lock
- 트랜잭션이 데이터에 대한 공유 lock 연산을 실행하면, 해당 데이터에 read 연산을 수행할 수 있지만 write 연산은 실행할 수 없다.
- 그리고 해당 데이터에 다른 트랜잭션도 공용 lock 연산을 동시에 실행할 수 있다.
- 데이터에 대한 사용권을 여러 트랜잭션이 함께 가질 수 있음
- 트랜잭션에서 읽을 목적으로 데이터 항목을 접근할 때는 Shared Lock을 요청함
전용(exclusive) lock
- 트랜잭션이 데이터에 전용 lock 연산을 실행하면 해당 데이터에 read 연산과 write 연산을 모두 실행할 수 있다.
- 그러나 해당 데이터에 다른 트랜잭션은 공용이든 전용이든 어떤 lock 연산도 실행할 수 없다.
- 트랜잭션에서 갱신을 목적으로 데이터 항목을 접근할 때는 Exclusive Lock을 요청함
기본 락킹 규약
- 락킹 기법을 사용해 트랜잭션이 데이터베이스에 있는 데이터에 접근하는 연산을 실행하려면 먼저 해당 데이터에 lock 연산을 실행하여 독점권을 획득해야 함.
- 일반적으로 데이터에 접근이 필요한 연산은 read 또는 write.
- 즉 read write를 실행하기 전에는 lock을 걸어야 함.
- 트랜잭션이 lock 연산을 통해 독점권을 획득한 데이터에 대한 모든 연산을 수행하고 나면 unlock 연산을 실행해서 독점권을 반납해야 함.
2단계 락킹 (2-Phase Locking Protocol)
로크를 요청하는 것과 로크를 해제하는 것이 2단계로 이루어지는데, 로크 확장 단계가 지난 후에 로크 수축 단계에 들어가게 됨 일단 로크를 한 개라도 해제하면 로크 수축 단계에 들어감
로크 확장 단계(Growing Phase - 1단계)
- 로크 확장 단계에서는 트랜잭션이 데이터 항목에 대하여 새로운 로크를 요청할 수 있지만 보유하고 있던 로크를 하나라도 해제할 수 없음
- 즉, 새로운 lock 연산만 수행할 수 있는 단계.
로크 수축 단계 (Shrinking Phase - 2단계)
- 로크 수축 단계에서는 보유하고 있던 로크를 해제할 수 있지만 새로운 로크를 요청할 수 없음
- 로크 수축 단계에서는 로크를 조금씩 해제할 수도 있고, 트랜잭션 완료 시점에 이르렀을 때 한꺼번에 모든 로크를 해제할 수 도 있음
- 일반적으로 한 번에 해제하는 방식을 사용
- 즉, unlock 연산만 수행할 수 있는 단계.
로크 포인트는 한 트랜잭션에서 필요로 하는 모든 로크를 걸어놓은 시점
데드록 (DeadLock)
- 2단계 락킹 프로토콜에서는 데드록이 발생할 수 있는데, 데드록은 두 개 이상의 트랜잭션들이 서로 상대방이 보유하고 있는 로크를 요청하면서 기다리고 있는 상태를 말함.
- 데드록을 해결하기 위해서는 데드록을 방지하는 기법이나, 데드록을 탐지하고 희생자를 선정하여 데드록을 푸는 기법을 사용함.
데드록 예시
- T1이 Student에 대해 독점 로크를 요청하여 허가받음
- T2이 Grade에 대해 독점 로크를 요청하여 허가받음
- T1이 Grade에 대해 공유 로크나 독점 로크를 요청하면 로크가 해제될 때까지 기다리게 됨
- T2가 Student에 대해 공유 로크나 독점 로크를 요청하면 로크가 해제될 때까지 기다리게 됨
- 데드록 탐지 후 Student, Grade 중 하나를 희생자로 선정해 데드록을 해제함.
다중 로크 단위
- 대부분의 트랜잭션들이 소수의 투플들을 접근하는 데이터베이스 응용에서는 투플 단위로 로크를 해도 로크 테이블을 다루는 시간이 오래 걸리지 않음
- 트랜잭션들이 많은 투플을 접근하는 데이터베이스 응용에서 투플 단위로만 로크를 한다면 로크 테이블에서 로크 충돌을 검사하고, 로크 정보를 기록하는 시간이 오래 걸림
- 트랜잭션이 접근하는 투플의 수에 따라 로크를 하는 데이터 항목의 단위를 구분하는 것이 필요함
- 한 트랜잭션에서 로크 할 수 있는 데이터 항목이 두 가지 이상이 있으면 다중 로크 단위라고 말함
- 데이터베이스에서 로크 할 수 있는 단위로는 데이터베이스, 릴레이션, 디스크 블록, 투플 등
락킹 단위와 병행성은 트레이드오프 관계
락킹 단위가 작아질수록 제어가 어렵고, 락킹 단위가 커질수록 병행성은 낮아지지만 제어가 쉬움.
마무리
이번 포스팅을 끝으로 데이터베이스의 트랜잭션에 대한 내용은 마무리할까 한다. 데이터베이스 분야도 정말 다양하고 깊은 내용들이 많기 때문에 현재 내가 필요한 수준 그리고 기본적인 내용만 공부한 후 담아냈다. 그리고 앞으로는 실제 개발에 데이터베이스를 활용하면서 필요한 부분만 골라 추가로 학습 후 정리하고자 한다.
참고
[Database] 8. 트랜잭션, 동시성 제어, 회복
DBMS Concurrency Control: Two Phase, Timestamp, Lock-Based Protocol
Cascading Rollback | Cascadeless Schedule
'Computer Science > DataBase' 카테고리의 다른 글
[DataBase] SQL vs NoSQL (0) | 2020.10.26 |
---|---|
[DataBase] DB를 지탱하는 트랜잭션 (0) | 2020.03.29 |
[DataBase] DB 성능을 위한 Index (0) | 2020.03.14 |