논리적 작업의 단위, 일관성이 보장되어야 한다.
Atomicity (원자성)
트랜잭션을 구성하는 모든 연산은 모두 실행이 되거나 아무것도 실행되지 않아야 한다.
Consistency (일관성)
트랜잭션이 실행된 후에도 일관성 있는 데이터베이스로 유지되어야 한다.
Isolation (격리성)
트랜잭션 수행 시 다른 트랜잭션의 연산이 끼어들 수 없다.
Durability (지속성)
성공적으로 수행된 트랜잭션은 이후 시스템 장애, 업데이트 등 변화에 관계없이 영원히 데이터베이스에 반영되어야 한다.
Autocommit mode (default)
statement가 시작할 때 transaction이 내부적으로 시작하고 매 statement가 끝날때마다 commit이 자동으로 실행된다.
SET AUTOCOMMIT = OFF
connection.setAutoCommit(false) // JDBC
Explicit mode
START TRANSACTION; -> Transaction 시작
COMMIT; -> Transaction의 commit에서 변경 내용을 데이터베이스에 저장하는 statement
ROLLBACK -> Transaction의 연산들로 변경된 내용을 취소하는 statement
START TRANSACTION;
UPDATE accounts SET balance = balance-5000 WHERE accID = "성호"
UPDATE accounts SET balance = balance+10000 WHERE accID = "은경"
COMMIT;
Isolation Level
트랜잭션의 분리 단계를 지정할 수 있다. 트랜잭션의 Isolation이 지켜지기 위해서는 a 트랜잭션에서 b 트랜잭션의 과정을 알아야 할 필요가 있고, 접근할 수 있는 단계를 설정할 수 있다.
READ UNCOMMITED
COMMIT이나 ROLLBACK 여부에 상관없이 다른 트랜잭션에서 값을 읽을 수 있다. 가장 낮은 단계의 isolation level이다.
정합성에 문제가 있을 수 있기 때문에 추천하지는 않는다.
Transaction 1의 Commit이 되기 전이지만 update된 값을 읽을 수 있다. 이를 Dirty Read
현상이라고 한다.
READ COMMITED
Mysql 등 대부분의 RDB에서 기본적으로 사용되는 isolation level이다. 데이터를 읽을 때 Undo 영역에 백업된 레코드를 읽는다.
READ UNCOMMITED와 같은 Dirty read 현상은 일어나지 않고 commit된 데이터만 읽을 수 있게 된다.
하지만 여기에서도 문제는 존재한다. Transaction 2에서 데이터를 2번 읽는다고 생각해보자.
Transaction 2는 하나의 Transaction에서 같은 쿼리를 날렸을 때, 각각 다른 값을 가져오게 된다.
REPEATABLE READ
트랜잭션마다 id를 부여해 트랜잭션 ID보다 작은 트랜잭션 ID에서 변경한 것만 읽는다. 이 역시 Undo 영역에 백업된 데이터를 읽게 된다.
Transaction 2는 해당 트랜잭션이 완료될때까지 Transaction ID를 기준으로 데이터를 읽기 때문에 한 트랜잭션 안에서 데이터의 정합성이 보장된다.
그러나 Undo 영역에 백업되는 데이터가 많아져 자칫 쿼리의 처리성능이 떨어질 수 있고, PHAMTOM READ와 같은 부작용이 일어날 수 있다.
SERIALIZABLE
트랜잭션이 완료될 때까지 SELECT문이 사용되는 모든 데이터에 공유 잠금을 건다.
먼저 작업이 선행된 트랜잭션의 데이터를 후행 트랜잭션이 갱신, 삭제하지 못할 뿐만 아니라 새로운 레코드를 삽입하는 것도 막기 때문에 완벽한 읽기 일관성을 보장한다.
PHANTOM READ
Alice와 Bob이 각각 트랜잭션을 갖고 DB에 접근한다.
Bob의 트랜잭션이 먼저 시작한다.
Bob은 SELECT문을 날려 모든 row를 조회하고 그 중 post_id가 1인 레코드만을 반환받는다. Bob은 총 3개의 row를 반환받는다.
Alice의 트랜잭션이 시작된다. id가 4고 post_id가 1인 레코드를 추가한다.
Alice의 트랜잭션이 종료된다.
Bob의 트랜잭션은 아직 끝나지 않은 상태에서 다시 동일한 쿼리를 날리게 되면 다른 결과를 반환받게 된다.
Non-Repeatable read
phamtom read와 non-repeatable read는 비슷해보이지만 차이점이 있다.
Non-Repeatable read (UPDATE)
START TRANSACTION; # Transaction 1
SELECT * FROM Member WHERE name = 'psw' # 1 row
START TRANSACTION; # Transaction 2
SELECT * FROM Member WHERE name = 'psw' # 1 row
UPDATE Member SET name = 'psy' WHERE name = 'psw' # update
COMMIT;
UPDATE Member SET name = 'g-dragon' WHERE name = 'psw' # o row affected
COMMIT;
2번 트랜잭션에서 psw를 psy로 변경한 뒤 COMMIT을 날리게 되면 name = 'psw'
의 내용을 undo에 남겨놓게 된다. 그래야 아직 완료되지 않은 트랜잭션에서 undo를 보고 psw를 조회할 수 있기 때문이다.
2번 트랜잭션이 완료된 이후에 1번 트랜잭션에서 name = 'psw'
를 다시 조회하게 되는데, undo 영역의 데이터를 읽어서 변경을 하려고 하면 문제가 발생한다. UPDATE는 변경을 수행할 ROW에 대해 쓰기 잠금을 걸어야 하는데, undo 영역의 데이터는 그게 불가능하기 때문이다.
결국 UPDATE문에서 name = 'psw'
를 찾을 수 없게 되고 변경이 일어나지 않게 된다.
Dirty Read
커밋되지 않은 데이터가 수정될 때 다른 트랜잭션에서 접근해 커밋 전과 후의 결과가 달라지는 현상
https://bit.ly/3FVdhDa
본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.