데이터베이스 트랜잭션 이해하기
What is a Database Transaction
데이터베이스는 유저의 계정 정보, 거래 내역, 제품 정보 등 서비스를 위한 중요한 데이터가 저장되는 공간이며 이 데이터는 모두 신뢰할 수 있어야 합니다. 이를 위해 데이터베이스는 다양한 장치를 제공하고 있습니다.
이번 포스팅에서는 데이터베이스 주요 기능 중 하나인 트랜잭션에 대해 살펴봅니다.
Transaction
트랜잭션의 사전적 의미는 거래입니다. 데이터베이스에서 트랜잭션은 하나의 거래를 안전하게 처리하도록 보장해 주는 것을 의미합니다.
하나의 거래를 안전하게 처리하기 위해서는 고려해야 하는 부분이 아주 많습니다. 간단한 예로, A 계좌에서 5,000원을 B 계좌로 송금한다고 하면, A 계좌에서는 5,000원이 감소하고 B 계좌에서는 5,000원이 증가해야 합니다.
계좌이체 프로세스
1. A 계좌에서 B 계좌로 5,000원 송금 요청
2. A 계좌의 잔고 5,000원 감소
3. B 계좌의 잔고 5,000원 증가
그런데 여기서 A 계좌의 잔고 5,000원 감소 처리까지는 완료했는데 도중에 어떠한 문제로 프로세스가 중단되어 B 계좌의 잔고 5,000원 증가 처리가 이루어지지 않았다면 데이터의 정합성이 깨지는 정말 치명적인 이슈가 될 것입니다.
따라서, 논리적인 하나의 작업 묶음은 마치 하나의 프로세스로써 동작해야 합니다. 모든 작업이 성공하는 경우에만 데이터 Commit이 수행되어야 하며 도중에 하나라도 실패하는 경우에는 Rollback이 발생해야 합니다. 이것을 가능하게 하는 것이 바로 데이터베이스 트랜잭션입니다.
ACID
데이터 처리는 서비스 운영에서 아주 중요한 부분이기 때문에 트랜잭션은 ACID를 만족해야 합니다.
- Atomicity - 원자성
- 트랜잭션 내에서 실행한 작업들은 마치 하나의 작업인 것처럼 모두 성공 또는 모두 실패해야 합니다.
- Consistency - 일관성
- 모든 트랜잭션은 일관성 있는 데이터베이스 상태를 유지해야 합니다.
- 데이터베이스에서 정한 무결성 제약 조건을 항상 만족해야 합니다.
- Isolation - 격리성
- 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리되어야 합니다.
- 격리성은 동시성과 관련된 성능 이슈로 인해 트랜잭션 격리 수준을 선택할 수 있습니다.
- Durability - 지속성
- 트랜잭션을 성공적으로 끝내면 그 결과가 항상 기록되어야 합니다.
- 중간에 시스템에 문제가 발생하더라도 데이터베이스 로그 등을 사용해 성공한 트랜잭션을 복구할 수 있어야 합니다.
Read Phenomena
트랜잭션 간의 격리성을 완벽히 보장하려면 트랜잭션을 거의 순차적으로 실행해야만 하는데, 이렇게 하면 수많은 유저들을 위한 서비스를 제공하는 것이 거의 불가능하기 때문에 현실성이 없습니다. 그래서 상황에 따른 트랜잭션 격리 수준이 등장하게 된 것 같습니다.
이 트랜잭션 격리 수준을 이해하려면 항상 함께 언급되는 세 가지 부정합 이슈에 대해 먼저 살펴볼 필요가 있습니다.
- DIRTY READ: 어떤 트랜잭션에서 처리한 작업이 완료되지 않았는데도 다른 트랜잭션에서 볼 수 있는 현상입니다.
- NON-REPEATABLE READ: 다른 트랜잭션에서 발생한
COMMIT
에 영향을 받아, 하나의 트랜잭션 내에서 똑같은 조회 쿼리를 반복해서 실행했을 때 같은 결과를 가져오지 못하는 현상입니다. - PHANTOM READ: 다른 트랜 잭션에서 수행한 변경 작업에 의해 데이터베이스 레코드가 보였다 안 보였다 하는 현상입니다.
Transaction Isolation Level
이와 같은 데이터 부정합 현상을 상황에 따라 적절하게 컨트롤 할 수 있도록 ANSI 표준에서 격리 수준을 4단계로 정의하였습니다.
READ UNCOMMITTED
각 트랜잭션에서의 변경 내용이 COMMIT
및 ROLLBACK
여부에 상관없이 다른 트랜잭션에서 조회가 가능합니다.
- ⛔ DIRTY READ
- ⛔ NON-REPEATABLE READ
- ⛔ PHANTOM READ
READ COMMITTED
COMMIT
이 완료된 데이터만 다른 트랜잭션에서 조회가 가능합니다.
- ✅ DIRTY READ
- ⛔ NON-REPEATABLE READ
- ⛔ PHANTOM READ
REPEATABLE READ
해당 트랜잭션 이전에 COMMIT
이 완료된 데이터만 조회하여 동일한 트랜잭션 내에서의 데이터 조회시 항상 동일한 결과를 보장합니다.
- ✅ DIRTY READ
- ✅ NON-REPEATABLE READ
- ⛔ PHANTOM READ
SERIALIZABLE
한 트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서는 절대 접근할 수 없습니다.
- ✅ DIRTY READ
- ✅ NON-REPEATABLE READ
- ✅ PHANTOM READ
격리 단계가 올라갈수록 격리 수준은 강화되지만 대신 성능이 저하되는 트레이드오프가 발생할 수 있습니다. 하지만, 실제로 SERIALIZABLE 격리 수준이 아니라면 큰 차이는 없습니다.
Transaction Commit and Rollback
데이터베이스에서 데이터를 수정하고 나서 이를 저장하는 과정은 일반 문서 수정과 유사합니다. 데이터 추가 또는 업데이트 쿼리를 실행하고 나서 이를 실제로 데이터베이스에 반영하기 위해서는 Commit을 수행합니다. 만약, 어떤 문제가 있어 변경한 내역을 저장하지 않으려면 Rollback을 수행합니다.
- Commit: 커밋은 모든 작업이 성공한 경우, 데이터베이스에 작업 결과를 정상 반영합니다.
- Rollback: 롤백은 작업 도중 문제가 발생한 경우, 데이터베이스를 작업 이전의 상태로 완전히 되돌립니다.
Setting Commit Mode
데이터베이스에 Commit을 수행하는 방식은 자동과 수동 방식이 있습니다. 설정한 Commit 모드는 해당 세션 내에서 지속되며 중간에 다시 변경하는 것 역시 가능합니다.
자동 커밋은 쿼리를 실행하면 그 직후에 자동으로 Commit이 수행됩니다.
-- Enable autocommit
set autocommit true;
insert into member(member_id, money) values ('data1',10000); -- 자동 커밋
insert into member(member_id, money) values ('data2',10000); -- 자동 커밋
수동 커밋 모드는 쿼리를 실행하고 나서 Commit 또는 Rollback을 직접 호출하는 방식입니다.
-- Disable autocommit
set autocommit false;
insert into member(member_id, money) values ('data3',10000);
insert into member(member_id, money) values ('data4',10000);
commit; -- 수동 커밋
일반적으로 데이터베이스 기본 설정이 자동 커밋 모드로 되어 있어서, 개발자가 수동 커밋 모드로 설정하는 것을 트랜잭션을 시작한다고 표현하기도 합니다.
- Database
- MySQL