트랜잭션이란?
데이터베이스의 상태를 변화시키기 위해 수행하는 작업 단위로,작업의 완전성 을 보장해주는 것이다.
즉, 논리적인 작업 셋을 모두 완벽하게 처리하거나(Commit) 또는 처리하지 못할 경우에는 (Rollback, savepoint) 원 상태로 복구해서 작업의 일부만 적용되는 현상이 발생하지 않게 만들어주는 기능이다. 사용자의 입장에서는 작업의 논리적 단위로 이해를 할 수 있고 시스템의 입장에서는 데이터들을 접근 또는 변경하는 프로그램의 단위가 된다.
트랜잭션의 특징
-
원자성(Atomicity): 트랜잭션이 DB에 모두 반영되거나, 혹은 전혀 반영되지 않아야 된다.
-
일관성(Consistency): 트랜잭션의 작업 처리 결과는 항상 일관성 있어야 한다.
-
독립성(Isolation): 둘 이상의 트랜잭션이 동시에 병행 실행되고 있을 때, 어떤 트랜잭션도 다른 트랜잭션 연산에 끼어들 수 없다.
-
지속성(Durability): 트랜잭션이 성공적으로 완료되었으면, 결과는 영구적으로 반영되어야 한다.
트랜잭션과 Lock(뭐가 다른데?!)
- 동시성을 제어하기 위해 Lock을 사용하며 여러 커넥션에서 동시에 동일한 자원(레코드나 테이블) 을 요청할 셩우 순서대로 한 시점에는 하나의 커넥션만 변경할 수 있게 해주는 역할을 한다.
- 논리적인 작업 셋 자체가 100% 적용 or 아무것도 적용되지 않아야 함을 보장하기 위해 트랜잭션을 사용한다.
트랜잭션의 상태
Active
트랜잭션의 활동 상태. 트랜잭션이 실행중이며 동작중인 상태를 말한다.
Failed
트랜잭션 실패 상태. 트랜잭션이 더이상 정상적으로 진행 할 수 없는 상태를 말한다.
Partially Committed
트랜잭션의 Commit 명령이 도착한 상태. 트랜잭션의 commit이전 sql문이 수행되고 commit만 남은 상태를 말한다.
Committed
트랜잭션 완료 상태. 트랜잭션이 정상적으로 완료된 상태를 말한다.
Aborted
트랜잭션이 취소 상태. 트랜잭션이 취소되고 트랜잭션 실행 이전 데이터로 돌아간 상태를 말한다.
🔒 Partially Committed 와 Committed 의 차이점
Commit 요청이 들어오면 상태는 Partial Commited 상태가 된다. 이후 Commit을 문제없이 수행할 수 있으면 Committed 상태로 전이되고, 만약 오류가 발생하면 Failed 상태가 된다. 즉, Partial Commited는 Commit 요청이 들어왔을때를 말하며, Commited는 Commit을 정상적으로 완료한 상태를 말한다.
- 트랜잭션은 꼭 필요한 최소의 코드에만 적용하는 것이 좋다. => 범위 최소화하기!
- 일반적으로 데이터베이스 커넥션은 개수가 제한적이다. 그런데 각 단위 프로그램이 커넥션을 소유하는 시간이 길어진다면 사용 가능한 여유 커넥션의 개수는 줄어들게 된다. 그러다 어느 순간에는 각 단위 프로그램에서 커넥션을 가져가기 위해 기다려야 하는 상황이 발생할 수도 있는 것이다.
트랜잭션 격리 수준(Transaction Isolation Level)
Isolation Level의 필요성
데이터베이스는 ACID 특징과 같이 트랜잭션이 독립적인 수행을 하도록 한다.
따라서 Locking을 통해, 트랜잭션이 DB를 다루는 동안 다른 트랜잭션이 관여하지 못하도록 막는 것이 필요하다.
하지만 무조건 Locking으로 동시에 수행되는 수많은 트랜잭션들을 순서대로 처리하는 방식으로 구현하게 되면 데이터베이스의 성능은 떨어지게 될 것이다.
그렇다고 해서, 성능을 높이기 위해 Locking의 범위를 줄인다면, 잘못된 값이 처리될 문제가 발생하게 된다.
=> 효율적인 Locking이 필수!
Isolation Level 종류
-
Read Uncommitted(레벨 0)
- Select 문장이 수행되는 동안 해당 데이터에 Shared Lock이 걸리지 않는 계층
- 트랜잭션 처리중이거나 아직 commit되지 않은 데이터를 다른 트랜잭션이 읽는 것을 허용함
- 연산 이전 값을 읽을 수 있으므로 DB 일관성 유지가 불가능함(Dirty Read 발생)
-
Read Committed(레벨 1)
** 오라클 서버가 Default로 사용하는 계층임
- SELECT 문장이 수행되는 동안 해당 데이터에 Shared Lock이 걸리는 계층
- 트랜잭션이 수행되는 동안 다른 트랜잭션이 접근할 수 없어 대기하게 됨
- Commit이 이루어진 트랜잭션만 조회 가능해짐
- 하나의 트랜잭션 내에서 동일한 Select 쿼리를 실행했을 때 항상 같은 결과를 보장해야 한다는 Repeatable Read의 정합성에 어긋나게 됨(NON-REPEATABLE READ 발생)
-
Repeatable Read(레벨 2)
** MySql 서버가 Default로 사용하는 계층임
- 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 Shared Lock이 걸리는 계층으로, 트랜잭션이 시작되기 전에 커밋된 내용에 관해서만 조회할 수 있는 격리 수준
- 트랜잭션이 범위 내에서 조회한 데이터 내용이 항상 동일함을 보장함
- 다른 사용자는 트랜잭션 영역에 해당되는 데이터에 대한 수정이 불가능하다.
- 트랜잭션 도중 새로운 레코드 삽입이 일어나면 Phantom Read 발생
- 한 트랜잭션 안에서 일정 범위의 레코드를 두 번 이상 읽었을 때, 첫번째 쿼리에서 없던 레코드가 두번째 쿼리에서 나타나는 현상
-
Serializable(레벨 3)
- 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 Shared Lock이 걸리는 계층으로, 한 트랜잭션을 다른 트랜잭션으로부터 완전히 분리하는 격리 수준(읽기 작업도 Shared Lock을 획득)
- 완벽한 읽기 일관성 모드를 제공한다.
- 다른 사용자는 트랜잭션 영역에 해당되는 데이터에 대한 수정 및 입력이 불가능하다.
- 동시성이 중요한 데이터베이스에서는 거의 사용되지 않는다
🔒 동시성을 증가시키면 데이터 무결성에 문제가 발생하고, 데이터 무결성을 유지하면 동시성이 떨어지게 됨
🔒 레벨을 높게 조정할 수록 발생하는 비용이 증가하며 동시 처리 성능이 떨어짐
https://steady-coding.tistory.com/562 _ 트랜잭션 관련 질문 보기
교착상태(Deadlock)
- 두 개 이상의 트랜잭션이 특정 자원의 잠금(Lock)을 획득한 채 다른 트랜잭션이 소유하고 있는 잠금을 요구하면 아무리 기다려도 상황이 바뀌지 않는 상태가 되는데, 이를 교착상태라고 한다.
교착 상태 빈도 낮추는 방법
- 트랜잭션을 자주 커밋한다.
- 정해진 순서로 테이블에 접근한다. 위에서 트랜잭션 1 이 테이블 B -> A 의 순으로 접근했고, 트랜잭션 2 는 테이블 A -> B의 순으로 접근했다. 트랜잭션들이 동일한 테이블 순으로 접근하게 한다.
- 읽기 잠금 획득 (SELECT ~ FOR UPDATE)의 사용을 피한다.
- 한 테이블의 복수 행을 복수의 연결에서 순서 없이 갱신하면 교착상태가 발생하기 쉽다. 이 경우에는 테이블 단위의 잠금을 획득해 갱신을 직렬화 하면 동시성은 떨어지지만 교착상태를 회피할 수 있다.
https://d2.naver.com/helloworld/407507 _ 트랜잭션 처리를 위한 DBMS의 대응 방식 관련 링크