트랜잭션 (Transaction)

Lilac-_-P·2023년 4월 28일
0

스프링 DB

목록 보기
3/9

데이터를 저장할 때 단순히 파일에 저장해도 되는데, 굳이 데이터베이스에 저장하는 이유는 무엇일까?
다른 여러가지 이유들이 있겠지만, 가장 대표적인 이유는 DB는 트랜잭션이라는 개념을 지원하기 때문이다.

DB에서 트랜잭션은 하나의 논리적 기능을 안전하게 처리하도록 보장해준다. DB의 상태를 변환시키는 하나의 논리적 기능은 DB 내에서 여러 작업 또는 연산으로 이루어져있을 수 있다. 즉, 트랜잭션은 DB의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위 또는 한꺼번에 모두 수행되어야 할 일련의 연산들을 의미한다.

트랜잭션의 모든 작업이 성공해서 DB에 정상 반영하는 것을 Commit이라 하고, 작업 중 하나라도 실패해서 작업을 시작하기 이전의 상태로 되돌리는 것을 Rollback이라고 한다. Commit과 Rollback에 대해서는 뒤에서 좀 더 자세히 설명한다.

트랜잭션 ACID

트랜잭션은 DB에서 하나의 논리적 기능을 안전하게 처리하도록 보장하기 위해 ACID를 보장해야한다.

  • Atomicity(원자성) : 트랜잭션 내에서 실행한 작업들은 모두 성공하거나 모두 실패해야한다.
  • Consistency(일관성) : 모든 트랜잭션은 일관성있는 데이터베이스 상태를 유지해야한다.
  • Isolation(격리성) : 동시에 실행되는 트랜잭션들은 서로에게 영향을 미치지 않도록 격리되어야한다.
  • Durability(지속성) : 트랜잭션을 성공적으로 끝내면 그 결과는 항상 기록되어야한다.

참고.
트랜잭션은 원자성, 일관성, 지속성을 보장한다. 격리성의 경우에는 완벽히 보장할 수 있는 방법이 있지만, 트랜잭션 간의 격리성을 완벽히 보장하려면 동시 처리 성능이 매우 안좋아진다.

트랜잭션을 더 자세히 이해하기 위해서는 DB 서버 연결 구조와 DB 세션에 대해 알아야한다.

클라이언트가 DB 서버에 연결을 요청하고 커넥션을 맺게 되면, DB 서버는 서버 내부에 세션이라는 것을 만든다. 앞으로 해당 커넥션을 통한 모든 요청은 이 세션을 통해서 실행하게 된다. 즉, 개발자가 클라이언트를 통해 SQL을 전달하면 현재 커넥션에 연결된 세션이 SQL을 실행한다.

세션이 중요한 이유는 세션이 트랜잭션을 시작하고, Commit 또는 Rollback을 통해 트랜잭션을 종료하기 때문이다.
하나의 트랜잭션이 종료되면 그 이후에 세션은 새로운 트랜잭션을 다시 시작할 수 있다.
사용자가 커넥션을 닫거나, DBA가 세션을 강제 종료하면 세션은 종료된다.

만약 커넥션 풀을 사용한다면 커넥션 풀이 커넥션을 생성할 때 세션이 생성되므로, 커넥션 풀이 보유한 커넥션 개수만큼 세션이 생성된다.

Commit 과 Rollback

트랜잭션의 모든 작업이 성공해서 DB에 정상 반영하는 것을 Commit이라 하고, 작업 중 하나라도 실패해서 작업을 시작하기 이전의 상태로 되돌리는 것을 Rollback이라고 한다.

만약 데이터를 변경(등록, 수정, 삭제)하는 SQL을 실행하고 DB에 그 결과를 반영하려면 커밋 명령어인 commit을 호출해야하고, 결과를 반영하고 싶지 않으면 롤백 명령어인 rollback을 호출하면 된다.

commit은 데이터 변경 내용을 DB에 영구적으로 반영하고, rollback은 트랜잭션을 시작하기 전으로 DB의 상태를 되돌린다.

여기서 중요한 점은 커밋을 호출하기전까지는 임시로 데이터를 저장한다는 것이다. 해당 트랜잭션을 시작한 세션에게만 변경된 데이터가 보이고, 다른 세션에게는 변경된 데이터가 보이지 않는다.

참고
커밋하지 않은 변경된 데이터를 다른 곳에서 조회할 수 있다면, 데이터 정합성의 큰 문제가 발생한다.
그러므로, 커밋을 호출하기 전까지는 트랜잭션을 시작한 세션에게만 변경된 데이터를 보이게하는 것이 맞다.

자동커밋과 수동커밋

평소에 사용했던 DB를 생각해보면 내가 직접 커밋 또는 롤백을 호출하지 않았는데도 데이터를 변경하는 SQL이 잘 적용됐었다.

이것은 DB에서 기본적으로 자동커밋이 활성화되어있기 때문이다. 자동커밋에 의해서 SQL 쿼리를 하나하나 실행할 때마다 자동으로 커밋이 호출되었던 것이다. 즉, 쿼리를 하나 보낼 때마다 트랜잭션이 시작되었다가 자동커밋에 의해서 종료되는 것이다. 그런데 자동커밋을 사용하면, 여러 개의 쿼리가 포함된 논리적인 기능을 하는 트랜잭션을 사용할 수 없다. 따라서, 원하는 트랜잭션 기능을 사용하려면, 자동커밋 대신 수동 커밋을 사용해야한다.

보통 자동커밋 모드가 기본으로 설정된 경우가 대부분이기때문에, 수동커밋 모드로 설정하는 것을 트랜잭션을 시작하다고 표현할 수 있다.

참고.
수동 커밋 모드나 자동 커밋 모드는 한번 설정하면 해당 세션에서는 계속 유지된다. 그래서 상황에 맞게 모드를 되돌려놔야한다.

DB 락

DB에는 여러 개의 세션이 동시에 존재할 수 있기 때문에, 동시 접근과 관련된 문제가 생길 수 있다.

예를 들어, 세션1이 트랜잭션을 시작하고 데이터를 수정하는 동안 커밋을 수행하지 않았는데, 세션2가 동시에 같은 데이터를 수정하게 되면 여러가지 문제가 발생할 것이다. 이런 문제들을 방지하려면, 세션이 트랜잭션을 시작하고 데이터를 수정하는 동안에는 커밋이나 롤백 전까지 다른 세션에서 해당 데이터를 수정할 수 없게 막아야한다.

DB는 이런 문제를 해결하기 위해 락이라는 개념을 제공한다.
특정 세션이 특정 데이터에 대한 락을 쥐고 있으면, 락을 보유한 세션을 제외한 다른 세션들은 해당 데이터에 접근할 수 없게 하는 것이다. 만약 락을 보유하지 않은 세션이 데이터에 접근하려고 하면 락이 반환될 때까지 대기해야하며, 대기 시간이 길어지면 타임아웃이 발생하면서 데이터 접근에 실패하는 것이다.

참고.
DB의 데이터를 변경(등록, 수정, 삭제)하는 쿼리들은 락을 사용해서 동시에 여러 세션이 데이터를 변경 할 수 없다.
반면에 DB의 데이터를 조회하는 쿼리는 선택적으로 락을 사용한다. 락이 없어도 데이터를 조회할 수 있고, 만약 조회를 하면서 조회하는 데이터의 변경을 막고 싶다면 선택적으로 락을 사용할 수도 있다.

profile
열심히 하자

0개의 댓글