3장 쿼리 입문 에서 테이블 유형 4가지 중, 임시 테이블은 특정시점(트랜잭션이 끝날 때, DB세션이 닫힐 때 등)에 사라진다고 배웠었다. 트랜잭션에 대해 알아보자. (책의 내용만으로 조금 부족해서 여러 포스팅을 참고했다)
여러 글을 봤는데 내가 이해하기 가장 쉬운건 이거였다.
데이터베이스 상태를 바꾸는 작업 단위
단위 1개만 수행될 수도 있고, 여러개가 순차적으로 수행될 수도 있다. 하나의 트랜잭션은 Commit 되거나 Rollback 된다. (여기서 Commit 은 git의 commit과 유사하다고 생각하면 편하다)
내가 여태까지 쓴 모든 쿼리들이 각각 하나의 트랜잭션이었던 거다. 근데 왜 난 트랜잭션을 쓴 적이 없을까?
MySQL 8.0은 기본적으로 Autocommit mode다. 즉, 내가 서버한테 "나 수동으로 트랜잭션할꺼야" 라고 말해주지 않고 INSERT
, DELETE
, UPDATE
와 같은 쿼리를 쓰면 서버는 자동으로 commit을 했던 것.
SELECT
의 경우에는 데이터 수정이 일어나는 것이 아니므로, 트랜잭션이 굳이 필요없다.
데이터베이스 자체를 여러명의 사용자가 동시에 사용하기 때문이다.
여지껏 예로 들어왔던 DVD 대여점을 생각해보자. 내가 이번주의 영화 대여 내역을 보고서로 만들려고 쿼리를 작성하는 동안에도, 아래와 같은 작업들을 또 다른 직원이 수행하고 있을 수 있다.
그럼 내가 작성하고 있는 보고서는 어떤 테이블을 기준으로 나와야할까? 이런 기준 때문에 잠금이 필요하다.
데이터 서버가 데이터 자원의 동시 사용을 제어하는데 사용한다. DB 일부가 잠기면, 해당 데이터를 쓰거나write 읽으려는read 다른 사용자는 잠금이 해제될 때까지 기다려야 한다.
구글링을 해보니 lock에 대해서도 내용이 많은 것 같은데, 책에서는 간단히 다루고 있으니 나도 잠금이란 이런 것이구나 정도만 알고 넘어가본다...
스토리지 엔진이 어떤 거냐에 따라 잠금 단위는 달라질 수 있다. 기본적으로 3가지 수준만 살펴보면,
각각 접근방식의 장단점이 있다. 전체 테이블을 잠그면 부기는 간단하지만, 사용자 수가 증가함에 따라 대기시간이 길어진다. 행별로 잠그면 더 많은 부기가 필요하지만, 여러 사용자가 각자 다른 행에 대해 작업한다면 동일한 테이블도 수정할 수 있다.
트랜잭션의 상태는 이렇게 나눠질 수 있다.
commit
rollback
트랜잭션의 프로세스를 잘 이해할 수 있는 쿼리를 보자. 0001 계좌에서 500달러를 0002 계좌로 보내는 시나리오다.
START TRANSACTION ;
-- 0001 계좌에서 잔고가 충분한지 확인하고, 돈을 출금
UPDATE account
SET avail_balance = avail_balance - 500
WHERE account_id = 0001
AND avail_balance > 500 ;
-- 0002 계좌로 입금
IF <1개의 행이 이전 쿼리로 인해 업데이트됨>
THEN
UPDATE account
SET avail_balance = avail_balance + 500
WHERE account_id = 0002 ;
-- [case1] 정상작동 : DB에 영구 적용
IF <1개의 행이 이전 쿼리로 인해 업데이트됨>
THEN
COMMIT ;
-- [case2] 문제발생 : 이 거래의 모든 변경사항을 취소
ELSE
ROLLBACK ;
END IF ;
-- [case3] 자금부족 또는 업데이트 중 오류 발생
ELSE
ROLLBACK ;
END IF ;
아래 명령어를 통해 트랜잭션을 실행할 수 있다.
START TRANSACTION ;
기본적으로 MySQL은 Autocommit mode이므로 아래 명령어를 통해 이를 비활성화 할 수 있다.
SET AUTOCOMMIT = 0
일단 Autocommit mode가 비활성화되면 commit
명령어를 사용해서 서버가 변경사항을 영구적으로 적용하고, 잠금 등을 해재할 수 있도록 지시해야 한다.
트랜잭션 시작 이후의 모든 변경사항을 실행취소하려면 rollback
명령어를 실행하면 된다.
start transaction
명령어를 실행하면 이전 트랜잭션이 커밋된다.alter table
과 같은 SQL 스키마 문을 실행하면 현재 트랜잭션이 커밋되고 새로운 트랜잭션이 시작된다. Message: Deadlock found when trying to get lock; try restarting transaction
세이브포인트를 설정하면, 트랜잭션 전체를 롤백하지 않고 특정 위치(=세이브포인트)로 롤백할 수 있다.
모든 세이브포인트는 이름을 지정할 수 있기 때문에 단일 트랜잭션 내에서 여러 세이브포인트를 가질 수 있다.
SAVEPOINT 이름 ;
ROLLBACK TO SAVEPOINT 이름 ;
commit
을 실행해야한다. rollback
을 실행하면 트랜잭션 내의 모든 세이브포인트가 무시되고 전체 트랜잭션이 실행 취소된다.MySQL 8.0 버전에는 아래와 같은 스토리지 엔진이 포함되어 있다. 기본엔진은 InnoDB다.
ALTER TABLE 테이블이름 ENGINE = INNODB ;