ACID라고 하는 성질은 데이터베이스에서 어떤 하나의 데이터를 처리하는 데 필요한 트랜잭션을 위한 성질이다. 여기서 트랜잭션은 뭘까? 계좌이체의 결제 과정을 생각해보자.
위 과정을 진행한다고 했을 때 쿼리문을 아래처럼 쓸 수 있다.
거래가 일어날 때 실행되는 쿼리문
UPDATE accounts SET balance = balance - 10000 WHERE user ='구매자';
UPDATE accounts SET balance = balance + 10000 WHERE user ='판매자';
근데 만약에 계좌 이체 도중에 아래처럼 어떤 오류나 문제가 발생한다면...?
이런 상황에서는 어떻게 할 수 있을까?
전부 없었던 일로 해줘야 한다. 즉 데이터를 초기 상태로 되돌려줘야 한다. 이 초기 상태로 되돌려주는 일을 트랜잭션이 수행하는 것이다.
다시 말해 작업이 하나라도 실패를 하게 되면 트랜잭션도 실패이고, 모든 작업이 성공적이면 트랜잭션 또한 성공이다. 성공 또는 실패 라는 두 개의 결과만 존재하는 트랜잭션은, 미완료된 작업 없이 모든 작업을 성공해야 한다.
이 트랜잭션은 ACID라는 특성을 가지고 있는데, 트랜잭션이 안전하게 수행된다는 것을 보장하기 위한 성질이다.
트랜잭션은 DB에 모두 반영되거나, 전혀 반영되지 않아야 한다. 즉 완료되지 않은 트랜잭션의 중간 상태를 DB에 반영해서는 안 된다.
원자성이 보장되면 구매자의 돈이 계좌에서 빠져나갔는데 판매자의 계좌에 돈이 들어오지 않는 일이 없을 것이다.
트랜잭션 작업 처리 결과는 항상 일관성 있어야 한다. 데이터베이스는 항상 일관된 상태로 유지되어야 한다.
둘 이상의 트랜잭션이 동시 실행되고 있을 때, 어떤 트랜잭션도 다른 트랜잭션 연산에 끼어들 수 없다. 각각의 트랜잭션은 서로 간섭 없이 독립적으로 이루어져야 한다는 것이다.
트랜잭션이 성공적으로 완료되었으면 결과는 영구히 반영되어야 한다.
ACID 성질은 트랜잭션이 이론적으로 보장해야 하는 성질이고 실제로는 성능을 위해 성질 보장이 완화되기도 한다. 예를 들어 독립성을 완벽하게 보장하려고 하면 동일 데이터에 100개의 연결이 접근했을 시, 이 100개 연결을 순차적으로 실행해야 한다. 이때 동시성이 매우 떨어지는 문제가 발생하게 되는데, 동시성을 얻기 위한 한 가지 방법으로 트랜잭션의 격리 레벨 설정을 할 수 있다.
동시에 DB에 접근할 때 그 접근을 어떻게 제어할지에 대한 설정으로, 표준적으로 네 가지의 레벨이 존재한다.
① READ-UNCOMMITTED
② READ-COMMITTED
③ REPEATABLE-READ
④ SERIALIZABLE
밑으로 갈수록 격리 수준이 높아지지만 성능이 떨어진다. 데이터가 정합성과 성능이 반비례하므로 케이스에 맞게 적절히 선택하는 것이 중요하다.
커밋이 완료된 데이터만 다른 트랜잭션에서 조회 가능
트랜잭션이 이루어지는 동안 다른 사용자는 해당 데이터에 접근 불가
아직 커밋하지 않은 상태라면 트랜잭션 시작 전 데이터를 불러옴
커밋하면 변경된 데이터를 불러옴
READ-COMMITTED 레벨에서는 Non-Repeatable Read, Phantom Read 현상이 발생한다. 이 현상은 같은 트랜잭션 내에서 SELECT
문을 두 번 조회했는데, 두 값이 다른 값이 출력되는 데이터 불일치 문제를 말한다.
SELECT
문을 쓸 때 나타날 수 있는 현상이다. 해당 쿼리로 읽히는 데이터에 들어가는 행이 새로 생기거나 없어지는 현상이다.SELECT
만으로도 트랜잭션이 커밋될 때까지 모든 데이터에 잠금이 설정되어 다른 트랜잭션에서 해당 데이터를 변경할 수 없게 된다.Dirty Read | Non-Repeatable Read | Pahntom Read | |
---|---|---|---|
READ_UNCOMMITTED | ⭕ | ⭕ | ⭕ |
READ_COMMITTED | ❌ | ⭕ | ⭕ |
REPEATABLE_READ | ❌ | ❌ | ⭕ |
SERIALIZABLE | ❌ | ❌ | ❌ |