트랜잭션이란
데이터를 저장할 때 단순히 파일에 저장해도 되지만, 데이터베이스에 저장하는 가장 큰 이유입니다.
트랜잭션은 그대로 번역하면 거래인데, 데이터베이스에선 하나의 거래를 안전하게 처리하도록 보장합니다.
A가 B에게 5000원 이체한다면, 아래 두개의 작업이 하나처럼 이뤄져야합니다.
1. A의 잔고 5000원 감소
2. B의 잔고 5000원 증가
1번이 성공하고 2번이 실패하고 그대로 반영된다면 A의 잔고만 5000원이 감소하는 문제가 발생합니다. 데이터베이스는 트랜잭션을 적용해 거래 도중 하나라도 실패하면 거래 전의 상태로 되돌릴 수 있습니다.
모든 작업이 성공해서 데이터베이스에 정상 반영하는 것을 Commit이라하고, 작업 도중 하나라도 실패해 이전으로 되돌리는 것을 Rollback이라 합니다.
트랜잭션 ACID
원자성
트랜잭션 내에서 실행한 작업들은 마치 하나의 작업인 것처럼 모두 성공 하거나 모두 실패해야합니다.
일관성
모든 트랜잭션은 일관성 있는 데이터베이스 상태를 유지해야 합니다. 예를 들어 데이터베이스에서 정한 무결성 제약 조건을 항상 만족해야 합니다.
격리성
동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리합니다. 예를 들어 동시에 같은 데이터를 수정하지 못하도록 해야 합니다. 격리성은 동시성과 관련된 성능 이슈로 인해 트랜잭션 격리 수준(Isolation level)을 선택할 수 있습니다.
지속성
트랜잭션을 성공적으로 끝내면 그 결과가 항상 기록되어야 합니다. 중간에 시스템에 문제가 발생해도 데이터베이스 로그 등을 사용해서 성공한 트랜잭션 내용을 복구해야 합니다.
트랜잭션은 원자성, 일관성, 지속성을 보장하지만 격리성을 완벽히 보장할수록 병렬처리가 불가능해져 동시 처리 성능이 매우 나빠집니다. 따라서 ANSI 표준은 트랜잭션 격리 수준을 4단계로 정의했습니다.
트랜잭션 격리수준
READ UNCOMMITED(커밋되지 않은 읽기)
다른 세션에서 변경하고 커밋되지 않은 내역도 접근가능해 데이터의 부정합 이슈가 큽니다.
READ COMMITTED(커밋된 읽기)
커밋된 내역만 읽지만, 다른 세션의 트랜잭션 커밋 여부에 따라 같은 트랜잭션 내에서 같은 조회 시에 다른 결과를 얻을 수 있습니다.
REPEATABLE READ(반복 가능한 읽기)
READ COMMITTED와 비슷하지만 같은 트랜잭션 내에서 같은 조회를 할 때 다른 트랜잭션에서 수정중인 로우는 원본 테이블이 아닌 UNDO LOG 에 접근하기에 같은 결과를 얻습니다.
SERIALIZABLE(직렬화 가능)
여러 트랜잭션이 동일한 로우에 접근할 수 없으므로 데이터 부정합이 없지만 모든 트랜잭션이 순차적으로 처리되어 성능이 떨어집니다. 일반 SELECT 문도 배타락이 걸립니다.
데이터베이스 연결 구조와 DB 세션
- 사용자가 WAS나 DB 접근 툴 같은 클라이언트를 사용해 데이터베이스 서버에 접근할 수 있습니다. 클라이언트 DB 서버에 연결을 요청하고 커넥션을 맺고, 이때 DB 서버는 내부에 세션을 만들어 해당 커넥션을 통한 모든 요청은 세션을 통해 실행됩니다.
- 개발자가 클라이언트(애플리케이션)을 통해 SQL을 전달하면 커넥션에 연결된 세션이 SQL을 실행합니다.
- 세션은 트랜잭션을 시작하고 커밋 또는 롤백을 통해 트랜잭션을 종료합니다. 이후 새로운 트랜잭션을 다시 시작할 수 있습니다.
- 사용자가 커넥션을 닫거나, DB관리자가 세션을 강제로 종료하면 세션은 종료됩니다.
- 커넥션풀이 10개의 커넥션을 생성하면, 세션도 10개 만들어집니다.
애플리케이션의 트랜잭션 제어
A가 B에게 5000원 계좌이체를 할 때는 아래의 작업이 한번에 이루어져야합니다.
- A의 잔고 5000원 감소
- B의 잔고 5000원 증가
애플리케이션에서 여러 SQL을 하나의 트랜잭션으로 관리하려면 같은 커넥션에서 실행되야하는데 그 이유는 다음과 같습니다.
- DB 서버에서 SQL 실행은 DB 세션이 담당합니다.
- 동일한 DB 세션 내에서 여러개의 SQL을 한 개의 트랜잭션으로 관리합니다.
- DB 세션은 DB 커넥션을 통해 애플리케이션 DB 드라이버 커넥션과 연결됩니다.
- 동일한 DB 드라이버 커넥션을 통해 여러 SQL을 실행합니다.
애플리케이션에서 커넥션풀에서 커넥션을 빌려 SQL을 실행할 때, DB는 Auto Commit이 Default로 켜져있습니다. 따라서 애플리케이션에서 트랜잭션 제어는 아래의 과정을 거칩니다.
- 커넥션 풀에서 커넥션 빌려오기
- 커넥션의 Auto Commit 끄기
- 여러개의 SQL을 동일한 커넥션으로 실행하기
- - 1 중간에 예외처리 발생 시 롤백하기
- 2 모든 요청이 정상적으로 종료되면 커밋하기 - 커넥션의 Auto Commit 키기
- 커넥션풀에 커넥션 반납하기
정리
트랜잭션의 격리수준과 동시성 성능은 상반
DB 세션과 커넥션의 연관관계
애플리케이션 단에서 여러 SQL의 한 트랜잭션 관리는 동일한 커넥션에서 실행
참고
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-db-1/dashboard
https://mangkyu.tistory.com/299