프로그래밍/Database

[데이터베이스] 트랜잭션(Transaction)에대한 고찰

Jay Tech 2018. 12. 16. 17:23

데이터베이스 트랜잭션

데이터베이스 트랜잭션이란 데이터베이스의 상태를 변화시키는 프로그램의 작업 단위이다. 

1. SQL 표준에서의 Transaction
  • 제일 첫 번째 SQL 문이 자동으로 Transaction 의 시작
  • Commit을 호출하면 트랜잭션 종료
  • Rollback을 호출하면 트랜잭션 취소
  • AutoCommit 모드일 경우 SQL 문장 하나 단위로 Transaction

2. Transaction의 성질 (ACID)

2-1. 원자성 (Atomicity)
트랜잭션의 작업들이 모두 수행되거나 전혀 수행되지 않아야 한다.. (all or nothing)
트랜잭션이 부분적으로 수행된다면 데이터베이스에 반영되지 않아야 한다. 그렇지않다면 데이터베이스의 상태가 inconsistent해진다.

2-2. 지속성 (Durability)
트랜잭션이 끝나면 commit된 것들에 대해 책임을 지는것. 즉 지속가능해야 한다.
소프트웨어나 하드웨어가 고장이 나더라도 완료된 트랜잭션은 영구적으로 반영이 되어야 한다. 그러므로 recovery system이 필수적이다. 결국엔 database log를 통해 복구한다.

2-3. 독립성 (Isolation)

그림처럼 3단계와 6단계 사이에 어떤 트랜잭션 T2가 개입하게 했다. 그러면 부분적으로 업데이트된 (partially updated) 데이터베이스를 바라보게 된다. 결코 db는 inconsistent한 상태가 보여지면 안된다. 독립성은 하나의 트랜잭션 실행도중 다른 트랜잭션이 끼어들지 못하게 하는 것이다. (concurrency-control system) 그럼에도 불구하고, 멀티 트랜잭션을 돌리는 것은 많은 장점이있다. 그렇다면 Serializability를 보장해야한다. Serializability는 각 트랜잭션을 따로 수행한 결과과 동일해여야하고 트랜잭션 간의 순서는 무관하다. 


2-4. 일관성 (Consistency)

트랜잭션 도중에는 데이터베이스가 inconsistent할 수는 있다. 하지만 트랜잭션이 성공적으로 완료하면 그 데이터베이스는 일관성 있는 상태로 변환한다. 즉 integrity를 보장한다고한다. 여기서 데이터베이스의 초기상태의 가정은 state 0 (initial state)에서는 consistence하다고 가정한다. 

참고로 개별 트랜잭션의 일관성은 애플리케이션을 개발하는 프로그래머의 책임이다. 데이터베이스 자체는 Atomicity, Durability, Isolation을 책임진다.



3. 저장장치의 Durability & Atomicity


* Volatile storage (휘발성 저장장치)

system crash에 살아남지 못한다. 여기에는 메인메모리, 캐시 메모리 등이 있다.


* Non-volatile storage (비 휘발성 장치)

system crash에도 살아남을 수 있다. 여기에는 플래시 메모리, 마그네틱 디스크, 마그네틱 테잎 등이 있다. 


그러므로 트랜잭션이 durable하려면 안정적인 저장장치에 쓰여져야 한다. 마찬가지로 트랜잭션이 atomic하려면 안정적인 저장장치에 log가 기록되어야 한다. 



4. Transaction의 상태


트랜잭션이 성공적으로 종료된 것을 committed 라고 한다. 예를들어 누군가에게 돈을 보냈으면 그것은 committed이다. 잘못 보냈다고 다시 돌리고 싶어서 roll-back을 할 수 없다. 왜냐하면 이미 commit된 트랜잭션이기 때문이다.


트랜잭션의 상태를 보자.



Active : 초기 상태이다. 트랜잭션이 실행중인 상태를 말한다.


Partially committed : 마지막 연산까지 실행했고, commit하기 직전의 상태이다


Failed : 트랜잭션에 오류가 발생하여 중단된 상태이다.


Aborted : 트랜잭션이 비정상적으로 종료되어 roll back된 상태이다. 두 가지 옵션이 있는데 하나는 트랜잭션을 재시작하는 것이고 다른 하나는 트랜잭션을 킬 하는 것이다.


Committed : 트랜잭션이 성공적으로 종료한 상태이다.



5. 직렬 스케줄


직렬 스케줄이란 트랜잭션의 각 연산들을 순서대로 실행한 것을 직렬 스케줄이라고 한다. 


* 충돌 직렬 가능성 (Conflict Serializable)

동일한 데이터에 대해서 두 트랜잭션이 있다고 가정하자. 그 중 하나라도 Write연산이 있다면 그것은 충돌이라고 부른다. 하지만 Read, Read면 충돌이 아니다. Read, Read면 두 연산이 순서가 바뀌어도 결과는 같기 때문이다. 하지만 Write연산이면 두 연산의 순서가 바뀌게 되면 결과는 달라진다. 


즉 하나라도 write가 있으면 충돌이라고 부른다. 



스케줄3에서 스케줄6으로 가는 경우를 보자. 스케줄3과 스케줄6은 둘 다 같은 결과를 내기 때문에 conflict serializable을 보장한다. 그러므로 스케줄3은 conflict serializable하다고 한다. 


다음 그림을 보자. T3와 T4의 순서를 바꿀 수 없다. 이러면 conflict serializable하지 않다고 한다. 


6. Recoverable Schedule (복구가능한 스케줄)


다음 시나리오는 복구가능하지 않다. 복구 가능하려면 T8의 commit이 먼저 일어나야 하는데 T9이 A를 read하자마자 commit을 해버렸다. 이상황에서 T8이 abort되면 데이터베이스가 inconsistent 해진다. 복구가능 스케줄은 단순히 T9의 commit보다 T8의 commit이 먼저 일어나기만 하면된다. 그 다음 설명할 Casecadeless 스케줄과의 차이가 있다.


7. Cascade Schedule (캐스케이드 스케줄)


T10이라는 트랜잭션에서 fail이 일어나면 T11과 T12는 Roll back이 되어야 한다. 이것이 Cascading rollback이다. 

그래서 Cascadeless Schedule은 다른 트랜잭션이 READ하기 전에 Commit을 해야한다. 이것이 Recoverable Schedule과의 차이이다. 다시 이야기하면 Cascadeless schedule은 모두 Recoverable하다라고 말할 수 있다.


8. Levels of Consistency


아까 말했듯이 consistency는 중요하다고 말할 수 있다. 하지만 실제 상황에선 critical(중요한)하지 않은 부분들은 consistency가 낮게 구현하기도 한다. 예를들어 은행에 돈이 얼마얼마 정도 있어요 라는 작업은 엄청 정확한 수치를 필요로 하지 않는것 처럼 말이다. (weak level of consistency)


밑으로 갈수록 독립성 레벨이 높아진다. 


Read Uncommitted : Dirty Read를 허용한다. 여기서 Dirty Read란 Commit되지 않은 데이터 변경을 읽을 수 있다. 그러므로 serializable하지 않은 결과가 나올 수 있다.


이런식으로 T1수행중에 T2가 동시에 수행 가능하다. 결국 T1에서 업데이트하는도중에 T2가 읽기 때문에 T1->T2 혹은 T2->T1으로 나올 수 없는 결과가 나올 수 있다.


Read Committed : Dirty Read를 허용하지 않는다. 


즉 T1도중에 T2를 허용하지 않는다. 하지만 Nonrepeatable read이다. 즉 T2:S1 -> T1 -> T2:S2순서로 실행가능한데 동일한 select이지만 서로 다른 결과가 발생한다. Serializable하지 않은 스케줄이 발생가능하다.


Repeatable Read : 오직 commit된 레코드만 읽을 수 있고 반복적으로 읽혀도 같은 값을 반환한다. 그러나 트랜잭션이 serializable하지 않을 수 있다. 


마찬가지로 Dirty Read를 허용하지 않는다. 하지만 Repeatable Read를 보장한다.



문제는 이 상황에서 Phantom Read problem이 발생할 수 있다. 한 번 읽었던 튜플의 값은 변경되지 않지만 새로운 튜플이 추가될 수는 있다. T1에 의하여 추가된 튜플은 이전에 존재하지 않았으므로 T2:S1 -> T1 -> T2:S2 순서로 수행된다면 동일한 결과가 나오지 않게 된다.


Serializable : 항상 Serializable을 보장하고 Dirty Read를 허용하지 않고 Repeatable Read를 보장하고 Phantom Read가 없다.



Isolation Level을 보았는데 실제 DBMS별로 제공하는 레벨과 디폴트가 다르다. 

Oracle : Read Committed (default), Serializable만 제공한다.

MS-SQL : Read Uncommitted, Read Committed(default), Repeatable Read, Snapshot, Serializable을 제공한다.

MySQL : InnoDB에서만 사용가능하다. Repeatable Read가 default이다.

PostgreSQL : Read Committed(default), Serializable을 제공한다. (다른 모드는 제공되지만, 실제로는 두 가지로 동작한다)


9. Concurrency Control 구현방법


- 대부분 Lock을 이용하여 구현됨

Two-phased locking protocol, 문제는 Deadlock이 발생가능하다. (서로 순환적으로 lock을 대기하여 트랜잭션 수행이 정지됨)


- Transaction의 Isolation level이나 속성을 잘 지정해야 함.

높은 수준의 isolation level이 일반적으로 더 많은 lock을 요구한다.

또한 SET TRANSACTION READ ONLY로 설정한다. 이것은 READ 연산만 가능한 transaction의 정의인데 일반적으로 성능 향상에 도움을 준다. 참고로 Oracle의 경우 transaction level의 read consistency를 보장한다. 즉 dirty read가 없고 repeatable read하며 phantom read가 없다. 


- recovery는 별도의 log를 기록하여 관리한다. 그리고 checkpoint기법을 주기적으로 사용하여 recovery시간을 절약한다.


1 2 3 4 5 6 7 8 9 ··· 20