트랜잭션(Transaction)은 데이터베이스에서 하나의 논리적 기능을 수행하기 위해 필요한 연산들을 묶는 단위입니다. 데이터베이스에 장애가 발생했을 때 데이터를 복구하는 작업의 단위이기도 합니다.
시작, 진행, 종료 단계를 가지며 만약 중간에 오류가 발생하면 RollBack(시작 이전 단계로 되돌리는 작업)을 수행하고 데이터베이스에 제대로 반영하기 위해서는 Commit(이후 롤백이 되지 않음)을 진행합니다.
커밋(Commit)은 여러 쿼리가 성공적으로 처리되었다고 확정하는 명령어입니다.
Auto Commit
MySQL은 기본적으로 자동 커밋 설정이 되어 있어(롤백이 안됨) 롤백을 하기 위해서는 자동 커밋 설정을 해제해 주어야 합니다.
- Auto Commit 활성화
SET autocommit = 1; -- 또는 SET autocommit = ON;
- Auto Commit 비활성화
SET autocommit = 0; -- 또는 SET autocommit = OFF;
롤백(Rollback)은 트랜잭션으로 처리한 하나의 묶음 과정을 일어나기 전으로 되돌리는 명령어입니다.
START TRANSACTION;
SELECT * FROM tbl_sample;
INSERT INTO tbl_sample VALUES (~~~);
UPDATE tbl_sample SET sample_name = "심플" WHERE sample_code = 1;
DELETE FROM tbl_sample WHERE sample_code = 3;
commit;
-- or
rollback;
트랜잭션의 특성으로는 원자성, 일관성, 독립성, 지속성이 있으며 "ACID 특성"이라고 부릅니다.
원자성(atomicity)은 트랜잭션의 모든 연산이 정상적으로 수행되거나, 하나도 수행되지 않아야 하는 특성입니다.
예를 들어,
1000만원을 가진 홍철이가 0원을 가진 규영이에게 500만원을 이체한다고 가정하겠습니다. 그렇다면 결과는 홍철이와 규영이 모두 500만원을 가지게 될것입니다. 해당 과정은 다음과 같습니다.
1. 홍철이의 잔액을 조회한다.
2. 홍철이의 잔액에서 500만원을 뺀다.
3. 규영이의 잔액에서 500만원을 더한다.여기서 1~3의 과정 중 데이터베이스 사용자는 이 세 가지 과정을 볼 수도 참여할 수도 없습니다. 이 과정의 결과를 조회할 수 있을 뿐입니다.
만약 3번 과정이 발생하기 직전에 오류가 발생하면 어떻게 될까요?
홍철이는 500만원을 송금한 상태로 잔액이 반영되었지만, 규영이는 500만원 송금을 받지 못하는 상황이 발생합니다. 500만원이 증발하게 된 것입니다.이처럼 트랜잭션은 일련의 과정을 하나의 원자처럼 전 과정이 성공하던지 실패하던지 둘 중 하나의 결과만 나오도록 원자성을 보장해줍니다.
일관성(consistency)은 '허용된 방식'으로만 데이터를 변경해야 하는 것을 의미합니다.
데이터베이스에 저장된 모든 데이터는 여러 가지 조건, 규칙에 따라 유효함을 가져야 합니다.
예를 들어, (마이너스 통장이 없다는 가정)
홍철이는 1000만원이 있고 규영이는 0원을 가지고 있습니다. 여기서 규영이가 홍철이에게 500만원을 입금하고 싶어합니다. 하지만 규영이가 송금을 하는 일이 이뤄져선 안됩니다. 잔액은 0원 미만이 될 수 없기 때문입니다.
트랜잭션의 모든 연산이 완료되었을 때만 변경 사항을 Commit 합니다.
이처럼 트랜잭션은 동시에 여러 사용자나 애플리케이션이 데이터베이스를 공유하면서도 서로의 작업에 영향을 주지 않고, 허용된 방식으로 데이터를 변경하면서 일관성을 유지합니다.
격리성(isolation)은 트랜잭션 수행 시 다른 트랜잭션이 끼어들지 못하도록 하는 특성입니다.
복수의 트랜잭션은 서로 격리되어 마치 순차적으로 실행되는 것처럼 작동되어야 하고, 데이터베이스는 여러 사용자가 같은 데이터에 접근할 수 있어야 합니다. 실제로 순차적으로 실행되는 것은 아닙니다.
격리성은 여러 개의 격리 수준으로 격리성을 보장합니다.
격리 수준은 SERIALIZABLE, REPEATABLE_READ, READ_COMMITTED, READ_UNCOMMITTED가 있습니다.
그림 4-28과 같이, 아래로 갈수록 격리성이 강해지고 위로 갈수록 동시성이 강해집니다.
격리 수준 | DIRTY READ | NON-REPEATABLE READ | PHANTOM READ |
---|---|---|---|
READ UNCOMMITTED | O | O | O |
READ COMMITTED | O | O | |
REPEATABLE READ | O(InnoDB는 발생 X) | ||
SERIALIZABLE |
팬텀 리드(Phantom Read)
팬텀 리드(phantom read)는 한 트랜잭션 내에서 동일한 쿼리를 보냈을 때 해당 조회 결과가 다른 경우를 말합니다.예를 들어, 트랜잭션 A가 회원 테이블에서 Age가 12 이상인 회원들을 조회하는 쿼리를 보냈다고 가정하겠습니다. 이 결과로 3개의 레코드가 조회되었습니다. 이후 트랜잭션 B가 Age가 15인 회원을 INSERT합니다. 그러면 트랜잭션 A의 그다음 조회에서 세 개가 아닌 네 개의 튜플이 조회되는 것입니다.
반복 가능하지 않은 조회(Non-Repeatable Read)
반복 가능하지 않은 조회(non-repeatable read)는 한 트랜잭션 내의 같은 행을 두 번 이상 조회했을 때, 읽어온 데이터가 다른 경우를 의미합니다.예를 들어, 은행 계좌에서 돈을 인출하는 상황을 가정하겠습니다. 트랜잭션 A에서는 내 계좌의 잔고를 확인한 후 인출하려는 상황이고, 트랜잭션 B에서는 동시에 같은 계좌로 돈을 입금하는 상황입니다.
1. 트랜잭션 A가 계좌의 잔고를 확인합니다. ( 예: 100만원)
2. 트랜잭션 B가 동시에 계좌에 50만원을 입금합니다.
3. 트랜잭션 A가 다시 계좌의 잔고를 확인하면, 계좌에 150만원으로 되어 있습니다.
더티 리드(Dirty Read)
더티 리드(Dirty Read)는 트랜잭션 처리 과정에서 아직 커밋되지 않은 수정 중인 데이터를 다른 트랜잭션에서 읽는 현상입니다. 이는 데이터 일관성 문제를 일으키는 원인이 될 수 있습니다.예를 들어, 트랜잭션 A는 특정 데이터를 수정하는 트랜잭션을 수행 중이고, 아직 커밋되지 않았습니다.
이 상태에서 트랜잭션 B가 같은 데이터를 읽는다면, 트랜잭션 A가 수정한 데이터를 읽게 됩니다.만약 트랜잭션 A가 롤백되면, 트랜잭션 B가 읽은 데이터는 실제로는 존재 하지 않는 것이 됩니다. 이는 데이터베이스의 일관성을 무너뜨립니다.
SERIALIZABLE은 트랜잭션을 순차적으로 진행시키는 것을 말합니다. 여러 트랜잭션이 동시에 같은 레코드에 접근할 수 없습니다. 그 결과로 어떠한 데이터 부정합도 발생하지 않습니다.
특정 트랜잭션이 사용중인 테이블의 행들을 다른 트랜잭션이 접근할 수 없도록 잠급니다. 그렇기 때문에 데드락이 일어날 확률도 많고 가장 성능이 떨어지는 격리 수준입니다. 가장 높은 데이터 정합성을 갖고 있으나, 성능이 가장 떨어지죠. 이 격리 수준에서는 단순한 SELECT 쿼리가 실행되더라도, 데이터 베이스 락이 걸려 다른 트랜잭션에서 데이터에 접근할 수 없게 됩니다.
데드락 - 두 개 이상의 작업이 서로 상대방의 작업이 끝나기 만을 기다리고 있기 때문에 결과적으로 아무것도 완료되지 못하는 상태
-- 해당 세션의 트랜잭션 격리 레벨을 설정하는 명령어이다.
set transaction_isolation = 'SERIALIZABLE'
SERIALIZABLE은 SELECT SQL문 실행시에서도 해당 레코드들에 공유 락(읽기 잠금, Shared Lock)을 진행합니다.
트랜잭션 A | 트랜잭션 B |
---|---|
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; | SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; |
START TRANSACTION; | |
(1건 조회 성공)SELECT * FROM member WHERE id >= 3; | |
START TRANSACTION; | |
(1건 조회 성공)SELECT * FROM member WHERE id >= 3; | |
COMMIT; | |
COMMIT; |
REPEATABLE_READ는 하나의 트랜잭션이 수정한 행을 다른 트랜잭션이 수정할 수 없도록 막아주지만 새로운 행을 추가하는 것은 막지 않습니다. 따라서 이후에 추가된 행이 발견(Phantom Read)될 수 있습니다.
MySQL 8.0의 InnoDB의 기본 격리 수준입니다. (InnoDB에서는 Phantom Read가 발생하지 않습니다.)
READ_COMMITTED는 가장 많이 사용되는 격리 수준이며 PostgreSQL, SQL Server, 오라클에서 기본 값으로 설정되어 있습니다. READ_UNCOMMITTED와는 달리 다른 트랜잭션이 커밋하지 않은 정보는 읽을 수 없습니다. 즉, 커밋 완료된 데이터에 대해서만 조회를 허용합니다.
하지만, Non-Repeatable read가 발생하여 트랜잭션 A가 수정한 레코드를 트랜잭션 B도 수정이 가능하여 두 번 이상의 조회 시 다른 결과 값을 조회할 수도 있습니다.
READ_UNCOMMITTED는 가장 낮은 격리 수준으로, 하나의 트랜잭션이 커밋되기 이전에 다른 트랜잭션에 노출되는 문제가 있지만 가장 빠릅니다. 이는 데이터 무결성을 위해 되도록이면 사용하지 않는 것이 이상적이나, 몇몇 행이 제대로 조회되지 않더라도 괜찮은 거대한 양의 데이터를 '어립잡아' 집계하는 데는 사용하면 좋습니다.
<롤백 시 문제>
지속성(Durability)은 성공적으로 수행된 트랜잭션은 영원히 반영되어야 하는 것을 의미합니다. 이는 데이터베이스에서 시스템 장애가 발생해도 원래 상태로 복구하는 회복 기능이 있어야 함을 뜻하며, 데이터베이스는 이를 위해 체크섬, 저널링, 롤백 등의 기능을 제공합니다.
- 체크섬
중복 검사의 한 형태로, 오류 정정을 통해 송신된 자료의 무결성을 보호하는 단순한 방법- 저널링
- 파일 시스템 또는 데이터베이스 시스템에 변경 사항을 Commit하기 전에 로깅하는 것
- 트랜잭션 등 변경 사항에 대한 로그를 남기는 것
무결성
무결성이란 데이터의 정확성, 일관성, 유효성을 유지하는 것을 말하며, 무결성이 유지되어야 데이터베이스에 저장된 데이터 값과 그 값에 해당하는 현실 세계의 실제 값이 일치하는지에 대한 신뢰가 생깁니다.