트랜잭션과 ACID

김회민·2023년 2월 25일
0

Spring Data

목록 보기
1/7

트랜잭션

START TRANSACTION;

UPDATE account SET balance = balance - 2000 WHERE id = 'J';
UPDATE account SET balance = balance + 2000 WHERE id = 'H';

COMMIT;

트랜잭션이란, DB쿼리의 최소한의 단위를 이야기한다.

하나의 트랜잭션에 여러개의 쿼리문이 존재할 수 있으며, 모두 성공하면 모든 쿼리문이 성공적으로 영구히 적용되며, 중간에 어떠한 이유로든 실패하게 된다면 모든 쿼리문이 취소되어 트랜잭션이 시작되기 전으로 되돌아간다. 영구히 적용되는 것을 Commit, 처음으로 되돌아가는 것을 Rollback이라고 말한다.

DB 서버에 여러개의 클라이언트가 동시에 액세스하게 될 경우, 데이터의 부정합이 발생할 가능성이 커지는데 이를 막고자 트랜잭션이라는 단위를 설정하여 부정합을 방지하고 일관적이고 지속적인 데이터 보관 및 수정이 용이하게 되었다.

위의 코드를 예로 들면, J의 계좌에서 H의 계좌로 2000원을 보낼때, J의 계좌에서 2000원을 차감한 쿼리문은 성공했지만, 어떤 이유로 인해 H 계좌에 2000원을 증가하는 쿼리문이 실패했다고 가정하자. 이 경우에 두 개의 쿼리문이 트랜잭션으로 묶여있지 않았다면 J 계좌에 2000원만 없어진 결과를 얻게된다. 이를 방지하고자 하나의 트랜잭션으로 묶어 두 번째 로직이 실패하는 경우 무조건 Rollback하도록 보장해야 한다.

AutoCommit

SELECT @@AUTOCOMMIT;
SET AUTOCOMMIT = FALSE;

DBMS에서는 AutoCommit을 지원하는데, 이는 하나의 쿼리문을 실행할 때마다 자동적으로 Commit을 수행하겠다는 의미이다.

보통 이 AutoCommit은 On으로 설정되어 있으며, 트랜잭션을 시작하기 위해서는 AutoCommit을 Off해주어야 여러개의 쿼리문을 수행할때 자동적으로 Commit되는 것을 막을 수 있게 된다. 때문에 트랜잭션을 시작한다는 의미는 AutoCommit을 Off 해준다는 의미과 같다고 말할 수 있다.

Trigger

DELIMITER $$

CREATE TRIGGER log_user_nickname_trigger
BEFORE UPDATE
ON users FOR EACH ROW
BEGIN
    INSERT INTO users_log VALUES(OLD.id, OLD.nickname, now());
END $$

DELIMITER ;

트리거란, 데이터가 변경되었을때 DBMS에서 자동적으로 실행되는 프로시저를 의미한다. 가장 대표적인 트리거는 유니크한 컬럼에 대한 중복된 데이터 검증과, 데이터 타입 검증이 있다.

사용자는 트리거를 이용해서 원하는 로직을 등록할 수 있는데, 예를 들어 특정 테이블에 INSERT, UPDATE가 수행되었을 시 이를 기록하기 위한 LOG 테이블에 자동적으로 INSERT하도록 구현할 수 있다.

  • BEFORE UPDATE ON users 키워드는 “users 테이블이 UPDATE 되기 전”이라는 뜻이다.
    • UPDATE 된 후에 적용하고 싶다면 AFTER 키워드를 사용하면 된다.
  • FOR EACH ROW 키워드는 변경된 ROW 마다 실행하라는 뜻이다.
  • OLD 키워드는 변경 전 tuple을 가리킨다.
    • 그와 반대되는 키워드는 NEW 가 있는데, 이는 변경 후 tuple을 가리킨다.
  • DELIMITER 키워드는 문장의 끝을 변경할 때 사용한다. 기본값은 ; 이다.
  • WHEN 키워드를 이용해 해당 트리거의 조건을 설정할 수 있다.

트리거를 사용할 때는 주의해야하는데, 그 이유는 Spring Application이 아닌 DBMS에서 발생하는 동작이기 때문에 파악하기 어렵고 문제가 생겼을 때 대응하기가 어렵기 때문이다. 또한, 여러 트리거가 연쇄적으로 발생할 가능성이 있고, 너무 많은 트리거를 설정하게 되면 DBMS 자체의 속도가 느려질 가능성이 있다.

ACID

원자성 ( Atomicity )

원자성이란, 하나의 트랜잭션이 모두 성공하거나 모두 실패하거나를 보장한다는 의미이다.

이 원자성을 보장하기 위해 DBMS에서는 트랜잭션이 시작되기 전에 변경되지 않은 데이터를 임시로 저장하고 Rollback이 수행되면 임시 저장된 데이터를 다시 불러온다.

일관성 ( Consistency )

일관성이란, 하나의 트랜잭션을 수행하고 나서 기존에 설정해두었던 모든 제약조건을 만족한다는 의미이다.

여기서 제약조건이란 기본키, 외래키, 데이터 타입, 유니크한 컬럼 등이 있다. 이 일관성을 보장하기 위해 DBMS에서는 Trigger를 통해 검사를 진행한다.

격리성 ( Isolation )

격리성이란, 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리한다는 의미이다.

격리성을 보장하기 위해 가장 기본적이고 안정적인 방법은 하나의 트랜잭션이 종료되기 전까지 해당 Row에 Lock을 걸어 다른 트랜잭션이 접근하지 못하도록 막는 것이다. 이를 Serializable이라고 하는데, 안정적인 대신 여러 트랜잭션이 동시에 실행하지 못하기 때문에 속도면에서 손해를 보게 된다. 때문에 SQL 표준에서는 격리성 수준을 4단계로 나누어 사용자가 원하는 수준을 선택하게 할 수 있게 지원한다.

지속성 ( Durability )

지속성이란, 트랜잭션을 성공적으로 끝내면 그 결과는 영원히 반영되어야 한다는 의미이다.

중간에 시스템에 문제가 발생하여 DB가 종료되더라도 해당 데이터는 데이터베이스 로그를 통해 복구되어져야 한다. 때문에 트랜잭션은 로그 저장이 완료된 시점까지 유지된다.

profile
백엔드 개발자 지망생

0개의 댓글