트랜잭션(Transaction)은 하나의 논리적 작업 단위를 보장하기 위한 DB 기능이다.
여러 개의 SQL이 하나의 작업처럼 처리되며, 전부 성공하거나 전부 실패해야 한다.
결제, 정산, 포인트 차감처럼 데이터 정합성이 중요한 영역에서는 트랜잭션이 필수다.
중간에 일부만 반영되면 데이터가 깨지고 복구가 어려워진다.
1. 트랜잭션의 기본 개념
트랜잭션은 BEGIN부터 COMMIT 또는 ROLLBACK까지의 구간을 의미한다.
이 구간 안의 작업은 하나의 묶음으로 처리된다.
START TRANSACTION;
UPDATE account SET balance = balance - 1000 WHERE id = 1;
UPDATE account SET balance = balance + 1000 WHERE id = 2;
COMMIT;
중간에 오류가 발생하면 ROLLBACK으로 이전 상태로 되돌린다.
ROLLBACK;
이 구조 덕분에 돈은 한쪽만 빠지고 다른 쪽은 증가하지 않는 상황을 방지할 수 있다.
2. ACID 특성
트랜잭션은 ACID라는 네 가지 특성을 가진다.
- Atomicity — 전부 성공하거나 전부 실패
- Consistency — 제약 조건을 유지
- Isolation — 동시에 실행돼도 서로 간섭 최소화
- Durability — 커밋 후 데이터는 보존
실무에서 특히 중요한 것은 Isolation이다.
동시 요청이 많을수록 격리 수준 설정이 성능과 정합성에 영향을 준다.
3. 격리 수준(Isolation Level)
MySQL InnoDB는 기본적으로 REPEATABLE READ를 사용한다.
격리 수준에 따라 발생 가능한 문제가 다르다.
SELECT @@transaction_isolation;
- READ UNCOMMITTED — Dirty Read 발생 가능
- READ COMMITTED — 커밋된 데이터만 조회
- REPEATABLE READ — 같은 트랜잭션 내 동일 결과 보장
- SERIALIZABLE — 가장 강한 격리, 성능 저하 가능
격리 수준을 높이면 안전하지만 락이 늘어나 성능이 떨어질 수 있다.
업무 특성에 맞는 수준을 선택해야 한다.
4. PHP에서 트랜잭션 처리 예시
애플리케이션 레벨에서도 명시적으로 트랜잭션을 제어해야 한다.
$pdo->beginTransaction();
try {
$stmt1 = $pdo->prepare('UPDATE account SET balance = balance - :amount WHERE id = :id');
$stmt1->execute([':amount' => 1000, ':id' => 1]);
$stmt2 = $pdo->prepare('UPDATE account SET balance = balance + :amount WHERE id = :id');
$stmt2->execute([':amount' => 1000, ':id' => 2]);
$pdo->commit();
} catch (Throwable $e) {
$pdo->rollBack();
throw $e;
}
예외 발생 시 반드시 rollBack()을 호출해야 한다.
try-catch 없이 beginTransaction만 쓰면 트랜잭션이 열린 채로 남을 수 있다.
5. 실무에서 자주 발생하는 문제
1) 트랜잭션 범위가 너무 넓은 경우
외부 API 호출, 파일 처리까지 트랜잭션 안에 넣으면 락이 오래 유지된다.
락 유지 시간이 길어질수록 다른 요청이 대기 상태에 빠진다.
트랜잭션은 DB 변경 구간만 최소한으로 묶는 것이 원칙이다.
2) Deadlock 발생
서로 다른 트랜잭션이 교차로 같은 자원을 점유하면 데드락이 발생한다.
MySQL은 한쪽을 강제로 종료한다.
SHOW ENGINE INNODB STATUS;
락 순서를 통일하거나, 업데이트 순서를 일관되게 유지하면 데드락을 줄일 수 있다.
3) 자동 커밋(auto commit) 오해
MySQL은 기본적으로 auto commit이 활성화되어 있다.
트랜잭션을 명시적으로 시작하지 않으면 각 쿼리가 자동 커밋된다.
여러 쿼리를 하나의 작업으로 묶어야 할 때는 반드시 beginTransaction()을 호출해야 한다.
6. 트랜잭션 설계 기준
- 데이터 정합성이 중요한 작업만 묶는다
- 트랜잭션 범위는 최대한 짧게 유지한다
- 락 순서를 일관되게 유지한다
- 외부 네트워크 호출은 트랜잭션 밖에서 처리한다
- 예외 발생 시 반드시 롤백한다
트랜잭션은 안전장치지만, 잘못 쓰면 성능 병목이 된다.
정합성과 성능 사이 균형을 고려해야 한다.
한 줄 요약
트랜잭션은 여러 작업을 하나로 묶어 정합성을 보장하는 기능. ACID를 이해하고, 범위를 최소화하며, 예외 시 롤백을 철저히 처리하면 안정성과 성능을 함께 유지할 수 있다.
댓글 0