정말 중요한 트랜잭션에 대해서 알아보자. 트랜잭션과 관련된 모든 것을 이 포스트에 다 정리해보겠다.
트랜잭션은 논리적인 작업이 DB에 모두 반영되거나(commit), 반영되지 않아야(rollback)함을 보장해 주는 것
이다. 이때 논리적인 작업은 하나의 쿼리일수도 있고, 여러개의 쿼리일 수도 있다.
이런 트랜잭션이 안전하게 수행되는것을 보장하기 위한 성질들이 있다. 원자성(Atomicity), 일관성(Consistency), 독립성(Isolation), 지속성(Durability)인데, 각각 하나씩 특성을 살펴보도록 하겠다.
트랜잭션으로 묶인 작업이 일부분만 실행되고 중단되지 않는 것을 보장하는 것이다.
트랜잭션으로 묶인 작업은 모두 실행되거나(commit), 모두 실행되지 않아야(rollback) 한다.
트랜잭션은 데이터베이스의 제약조건을 지키며 실행되어야 한다는 것이다. 트랜잭션에 있는 작업들 중 데이터베이스 제약조건을 지키지 않는 작업이 있으면 해당 트랜잭션은 중단되고 rollback된다.
예를들어 unique 제약조건을 지키지 않는 작업이 트랜잭션 내부에 있다면 해당 트랜잭션은 rollback된다.
동시에 실행되는 트랜젹션들이 서로에게 영향을 미치지 않도록 격리한다. 격리성은 동시성과 관련된 성능 이슈로 인해 격리 레벨을 선택할 수 있다.
트랜잭션을 성공적으로 끝내면 그 결과가 항상 기록되어야 한다. 중간에 시스템에 문제가 발생해도 데이터베이스 로그 등을 사용해 성공한 트랜잭션 내용을 복구해야 한다.
동시에 다수의 트랜잭션이 실행되기 때문에 트랜잭션의 독립성이 지켜지지 않는 문제가 발생한다. 독립성을 지키기 위해, 그리고 성능 저하를 조절하기 위해 트랜잭션엔 여러 격리레벨이 있다. 크게 "READ UNCOMMITTED", "READ COMMITTED", "REPEATABLE READ", "SERIALIZABLE" 4가지로 나눌 수 있다. 뒤로 갈수록 격리레벨이 엄격한 것인데, 성능은 SERIALIZABLE을 제외하고 크게 차이가 나지 않는다고 한다.
각 트랜잭션에서의 변경 내용이 COMMIT이나 ROLLBACK여부에 상관 없이 다른 트랜잭션에서 보여진다.
어떤 트랜잭션에서 처리한 작업이 완료되지 않았는데도 다른 트랜잭션에서 볼 수 있게 되는 현상을 Dirty Read라고 한다.
각 트랜잭션에서 변경 내용이 COMMIT되면 다른 트랜잭션에서 뵤여진다. 따라서 Dirty Read가 발생하지 않는다. READ COMMITTED에서 사용자가 변경한 내용이 다른 사용자에게 어떻게 조회되는지 아래 그림을 보자.
READ COMMITED 격리레벨에선 'non-repeatable read"라는 문제가 발생한다. 왜 이런 문제가 발생하는지 알아보자.
MySQL의 InnoDB 스토리지 엔진에서 기본적으로 사용되는 격리수준이다.
Non-Repeatable Read 부정합이 발생하지 않는다. InnoDB 스토리지 엔진은 트랜잭션이 Rollback될 가능성에 대비해 변경되기 전 레코드를 언두(Undo) 공간에 백업해두고 실제 레코드 값을 변경한다.이런 변경 방식을 MVCC라 하고, MVCC를 위해 언두 영역에 백업된 이전 데이터를 이용해 동일 트랜잭션 내에서 동일한 결과를 보여줄 수 있도록 보장한다.
REPEATABLE READ와 READ COMMITTED의 차이는 언두 영역에 백업된 레코드의 여러 버전 가운데 몇 번째 이전 버전까지 찾아 들어가야 하는지에 있다.
모든 InnoDB의 트랜잭션은 고유한 트랜잭션 번호(순차적으로 증가하는 값)를 가지며, 언두 영역에 백언된 모든 레코드에는 변경을 발생시킨 트랜잭션의 번호가 포함돼 있다. 그리고 언두 영역에 백업된 데이터는 InnoDB 스토리지 엔진이 불필요하다고 판단하는 시점에 주기적으로 삭제한다.
REPEATABLE READ 격리 수준에서는 MVCC를 보장하기 이해 실행 중인 트랜잭션 가운데 가장 오래된 트랜잭션 번호보다 트랜잭션 번호가 앞선 언두 영역의 데이터는 삭제할 수 없다. 그렇다고 가장 오래된 트랜잭션 번호 이전의 트랜잭션에 의해 변경된 모든 언두 데이터가 필요한 것은 아니다. 더 정확하게는 특정 트랜잭션 번호의 구간 내에서 백업된 언두 데이터가 보존해야 하는 것들이다.
트랜잭션이 길어지면 undo영역에 데이터가 계속 쌓여 성능이 떨어지는 문제가 발생할 수 있다.
격리레벨이 SERIALIZABLE로 설정되면 순수한 SELECT작업과 같이 원래 lock이 필요없는 작업도 lock을 획득해야 하며, 동시에 다른 트랜잭션은 그러한 레코드를 변경하지 못하게 된다.
즉, 한 트랜잭션에서 읽고 쓰는 레코드를 다른 트랜잭션에서 절대 접근할 수 없게 된다.
https://ko.wikipedia.org/wiki/ACID
Real My Sql
자바 ORM 표준 JPA 프로그래밍