트랜잭션은 "논리적으로 봤을 때 하나의 작업 단위" 를 의미합니다. 트랜잭션의 예로 가장 많이 드는 이체 작업을 생각해봅시다.
A가 B에게 20만원을 이체하려고 합니다. 간단하게 나누어서 살펴봅시다.
A의 계좌에서 20만원을 빼야합니다.(SQL문)
B의 계좌에 20만원을 더해주어야 합니다.(SQL문)
이 20만원을 이체하는 작업은 두 작업이 모두 성공해야 하는 작업입니다.
실제로는 두 가지의 작업을 하지만 논리적으로 봤을 때는 두 작업 모두를 성공해야만 의미가 있는 단일 작업으로 생각할 수 있습니다.
이러한 것을 트랜잭션이라고 합니다.
더 자세히 설명하면 이체처럼 어떤 작업을 위해서 수행되어야 하는 SQL문들이 모두 성공해야 하는 논리적 단일 작업이며,
이러한 SQL문들 중 일부만 성공해서는 DB에 반영되어서는 안되는 하나의 논리적 단일 작업을 트랜잭션이라 칭합니다.
트랜잭션을 사용하면서 알아야 하는 개념 세 가지가 있습니다.
바로 commit과 rollback, autocommit 입니다.
insert into account values ('A', 10000);
이 SQL문을 실행하면 자동으로 commit이 일어나고 account 테이블에 데이터가 영구적으로 저장되는 것입니다.
MySQL은 기본적으로 autocommit이 활성화 되어있으며, 다른 DBMS에서도 대부분 같은 기능을 제공합니다.
그럼 위에서 살펴본 이체의 예제를 다시 보면 두 개의 SQL문을 수행해야 하는데 autocommit이 활성화 되어있으면, A의 계좌에서 20만원을 빼는 작업을 하고 바로 commit이 일어나는 것으로 생각할 수 있습니다.
MySQL 기준 트랜잭션을 수행하기 위해서
start transaction
위 명령을 먼저 수행해야 하고 실행과 동시에 autocommit 기능이 비활성화 됩니다.
그렇기 때문에 하나의 sql문을 실행하고 바로 commit이 일어나지 않으며 트랜잭션을 위한 모든 sql문을 실행하고 commit까지 명시적으로 실행해주어야 합니다.
중간에 문제가 발생했다면 당연히 rollback 해주어야 합니다.
rollback 혹은 commit을 수행하면서 트랜잭션이 종료되면 다시 autocommit 기능은 활성화 됩니다.
ACID는 transaction 설명하기 위해서 빠질 수 없는 내용입니다.
ACID란 transaction이 어떠한 성질을 가져야 하는 지를 나타내는 단어들입니다.
하나씩 살펴봅시다.
원자성입니다. 원자란 화학분야에서 더 이상 쪼갤 수 없는 가장 작은 단위를 의미합니다.
더 이상 쪼갤 수 없다는 것이 포인트입니다.
앞에서 transaction을 설명할 때,
transaction이란 어떤 작업을 수행할 때 그것을 수행하기 위해 해야하는 일들이 있고, 이것들이 모두 성공해야만 의미가 있기 때문에 논리적으로 하나의 단일 작업을 의미한다고 했습니다.
이체라는 작업은 A의 계좌에서 돈을 빼고 B의 계좌에 돈을 더하는 두 가지 작업이(sql문들이) 모두 성공해야 하는 더 이상 쪼갤 수 없는 작업으로 transaction이라고 할 수 있습니다.
또한 All or nothing의 개념으로
논리적으로 쪼개질 수 없는 작업의 단위이기 때문에 sql문들이 모두 성공해야 하고,
중간이 하나의 작업이라도 실패했다면 지금까지의 작업을 모두 취소하여 아무 일도 없었던 것처럼 rollback해야 합니다.
commit과 rollback은 개발자가 실행하고 이 작업에 해당하는 동작들은 DBMS가 해줍니다.
개발자는 언제 commit 혹은 rollback을 할 지 결정해야 합니다.
하나의 sql문이라도 실패했다면 바로 rollback 할 수도 있지만, 다른 플랜 B의 작업으로 이어갈 수도 있기 때문입니다.
데이터의 일관성을 의미합니다.
우리는 데이터베이스 스키마를 생성할 때 제약사항이라는 것들을 걸어놓을 수 있습니다.
constraints, trigger 등을 통해 DB에 rule 등을 적용할 수 있는 것입니다.
transaction은 DB 상태를 consistent상태(우리가 정한 DB rule을 어기지 않은 상태)에서 벗어나지 않는 것을 보장해야 합니다.
transaction을 실행하면 DB에 정의된 rule을 위반했는지 안했는지 DBMS가 commit 하기 전에 확인하고 에러 메세지 등을 통해 알려줍니다.
이러한 발생한 에러 혹은 예외를 잘 처리해서 rollback을 시켜주어야 합니다.
그 외에 application 관점에서 transaction이 consistent하게 동작하는지는 개발자가 잘 살펴보아야 합니다.
격리성입니다.
여러 트랜잭션이 동시에 실행되면 우리가 의도한 것과는 다른 동작이 발생할 수 있습니다.
A가 B에게 20만원을 입금하려는 작업과 B가 자기 계좌에 30만원을 입금하려는 작업이 동시에 발생했다고 가정해봅시다.
transaction1
1. A의 계좌를 확인합니다. -> 100만원 있음
2. A의 계좌에서 20만원을 뺍니다.
3. B의 계좌를 확인합니다. -> 200만원 있음
여기서 transaction2가 참여합니다.
1. B의 계좌를 확인합니다. -> 200만원 있음
2. B의 계좌에 30만원을 입금합니다. 230만원이 됨
트랜잭션 종료
다시 transaction1
4. B의 계좌에 20만원을 더해줍니다. -> 220만원이 됨
트랜잭션 종료
B의 계좌에서 30만원이 사라져 버렸습니다.
여러 transaction들이 동시에 실행되니까 이러한 문제가 발생합니다.
Isolation이란,
여러 transaction들이 동시에 실행되더라도 단일로 실행되는 것처럼 동작하게 만드는 것입니다.
하지만 이러한 Isolation이 엄격하면 엄격할수록 격리 수준이 높아져서
다른 하나의 transaction이 다른 transaction에 의해 영향을 받을 확률이 줄어들게 됩니다.
하지만 DB 서버 퍼포먼스에 좋지 못하기 때문에 DBMS들은 여러 종류의 isolation level을 제공합니다.
반대로 isolation level이 낮으면 낮을 수록 동시성이 높아져서 하나의 transaction이 다른 transaction에 의해 영향을 받을 확률이 높아지지만 DB 서버의 퍼포먼스는 좋아집니다.
개발자들은 isolation level 중에서 어떤 level로 transaction을 동작시킬지 설정할 수 있습니다.
보통은 default로 놓고 쓰기도 하지만 상황에 따라 이 설정을 튜닝해야 할 필요도 있습니다.
concurrency control의 주된 목표가 isolation입니다.
영존성입니다.
transaction은 수행 후에 commit된 데이터들은 DB에 영구적으로 저장합니다.
기본적으로 transaction의 durability는 DBMS가 보장합니다. commit된 데이터를 비휘발성 메모리(디스크)에 저장하기 때문입니다.
transaction을 어떻게 정의해서 사용할 것인지는 개발자가 정하는 것이기 때문에 구현하는 기능과 ACID 속성을 이해해야 transaction을 잘 정의할 수 있습니다.
또한 DBMS에 default 설정이 있어서 기본적으로 동작하는 것들이 있지만 모든 걸 다 알아서 해주는 것은 아니기 때문에 transaction ACID와 관련해서 개발자가 챙겨야 하는 부분들이 있습니다.