여러 사용자가 동일한 데이터를 동시에 수정하려고 할 때 데이터베이스에서 제공하는 기능 중 하나가 격리수준이다. 데이터 무결성을 유지시키고 성능을 높이기 위해 필요하다.
동시에 여러 트랜잭션이 처리될 때, 트랜잭션끼리 서로 얼마나 고립되어있는 지를 나타내는 것이 트랜잭션의 격리 수준(isolation level)이다.
특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있도록 허용할지 말 지를 결정할 수 있다.
데이터베이스는 locking을 통해서 트랜잭션이 독립적인 수행을 하도록 해야한다. 즉, 특정 트랜잭션이 데이터베이스를 다루는 동안 다른 트랜잭션이 관여하지 못하도록 막아야 한다.
하지만 무조건 locking으로 동시에 수행되는 수많은 트랜잭션들을 순서대로 처리하는 방식으로 구현하게 되면 데이터베이스 성능은 떨어지게 되고.. 그렇다고 성능을 높이기 위해 locking의 범위를 줄이면 잘못된 값이 처리될 수 있다.
때문에 최대한 효율적인 locking 방법이 필요하다!!
격리 수준은 세 가지 수준을 가지고 있고, 대부분의 데이터베이스에서는 1단계인 Read Committed가 기본 수준이다.
0단계의 Read Uncommitted는 잘 사용되지 않고, PostgreSql에서는 지원하지 않는다.
각 트랜잭션에서의 변경 내용을 commit이나 rollback 여부에 상관 없이 다른 트랜잭션에서 값을 읽을 수 있다. 즉 트랜잭션에 처리중이거나, 아직 commit되지 않은 update된 데이터를 다른 트랜잭션이 읽는 것을 허용한다.
Select문이 수행되는 동안 해당 데이터에 Shared Lock이 걸리지 않는 계층이다. 데이터베이스의 일관성을 유지하는 것이 불가능하다. 정합성에 문제가 많은 격리 수준이기 때문에 사용이 권장되지 않는다.
트랜잭션 작업이 완료되지 않았는데도 다른 트랜잭션에서 볼 수 있게 되는 현상을 말한다.
예를 들어 A 트랜잭션에서 3번 학생의 성적을 B에서 A로 변경했고 아직 커밋하지 않았다.
이 때 B 트랜잭션에서 3번 학생의 성적을 조회했을 때 A를 읽게된다. (Dirty Read)
그런데 이후에 A 트랜잭션에서 문제가 발생해 Rollback이 일어났다.
하지만 B 트랜잭션에서는 3번 학생의 성적을 여전히 A로 생각하고 로직을 수행하게 된다.
데이터 정합성에 문제가 발생했다.
커밋된 데이터만 읽는 격리 수준으로, Oracle에서 기본적으로 사용되고 있다. Select 문이 수행되는 동안 해당 데이터에 Shared Lock이 걸린다.
트랜잭션이 수행되는 동안 다른 트랜잭션은 접근하지 못하고 대기한다.
실제 테이블 값을 가져오는 것이 아니라 Undo 영역에 백업된 레코드에서 값을 가져온다. 커밋하지 않는 데이터를 읽을 수 없기 때문에 Dirty read가 발생하지 않는다.
한 트랜잭션 안에서 같은 쿼리를 두 번 실행했을 때 다른 값이 나오는 Read 현상이다. 특정 데이터에 대한 수정이 발생하면 나타날 수 있다.
예를 들면 A 트랜잭션에서 3번 사원의 나이를 25에서 27로 변경했다.
만약 커밋하지 않은 상태에서 B 트랜잭션이 3번 사원의 나이를 조회한다면, undo 영역에 있는 25의 나이로 알게 될 것이다.
A 트랜잭션이 이후 변경사항을 커밋했는데, 아직 끝나지 않은 B 트랜잭션이 다시 3번 사원의 나이를 조회하게 되면, 이번에는 커밋된 27살의 나이로 조회하게 되는 것이다.
이는 하나의 트랜잭션 내에서 똑같은 쿼리를 실행했을 때 항상 같은 결과를 가져와야 하는 Repeatable Read의 정합성에 어긋난다.
이러한 문제는 주로 입금, 출금 처리가 진행되는 금전적인 처리에서 주로 발생하고 데이터 정합성이 깨지며 버그를 찾기 어려워진다.
트랜잭션이 완료될 때까지 select 문장이 사용되는 모든 데이터에 Shared Lock이 걸리는 계층으로, Mysql에서 기본적으로 사용되고 있다.
데이터 조회 시 항상 동일한 데이터 응답을 보장한다.
다른 사용자는 트랜잭션 영역에 해당되는 데이터에 대한 수정이 불가능하다.
Mysql에서는 트랜잭션마다 트랜잭션 id를 부여해서 자신의 트랜잭션 id보다 작은 트랜잭션 번호에서 커밋된 것만 읽게 된다.
데이터는 Undo 공간에 백업해두고 실제 레코드 값을 변경한다. 백업된 데이터는 불필요하다고 판단되는 시점에 주기적으로 삭제한다. Undo에 백업된 레코드가 많아지면 Mysql 서버의 처리 성능이 떨어질 수 있다. 이러한 변경 방식을 MVCC(Multi Version Concurrency Control)라고 한다.
Non-Repeatable Read 부정합이 발생하지 않는다.
예를 들면, id 10번 트랜잭션이 3번 상품의 가격을 조회했을 때 1000원의 값을 받았다.
그 후 12번 트랜잭션이 3번 상품의 가격을 1500원으로 변경하고 커밋했다.
10번 트랜잭션이 이후 다시 3번 상품의 가격을 조회했을 때, Undo 영역에 백업된 데이터가 반환되어 1000원의 가격을 다시 받는 것이다.
Undo 영역에 백업된 모든 레코드는 변경을 발생시킨 트랜잭션의 번호를 포함하고 있기 때문에, 자신의 트랜잭션 번호보다 낮은 트랜잭션 번호에서 변경된(커밋된) 값만 보게 된다.
Repeatable Read 수준에서, Shared Lock 상태의 데이터에 대해 변경 불가는 보장되지만, 새로운 데이터를 추가하거나 삭제하는 것은 가능하다.
Phantom Read는 한 트랜잭션 안에서 일정 범위의 레코드를 두 번 이상 읽었을 때, 다른 트랜잭션에서 수행한 변경 작업에 의해 어떤 레코드가 보였다가 안 보였다가 하는 현상을 말한다.
즉 트랜잭션 중에 외부 작업에 의해 없던 행이 추가되어 새로 입력된 데이터를 읽는다거나, 트랜잭션 중에 데이터가 삭제되어 다음 읽기 시 이전에 존재하던 행이 사라지는 것이다.
예를 들면, 10번 트랜잭션이 A 테이블의 행의 수를 count(*)로 조회한다고 했을 때, 결과값은 2였다.
그 후 12번 트랜잭션이 A 테이블에 insert를 실행하고 커밋했다.
10번 트랜잭션이 동일한 쿼리문으로 행의 수를 읽어온다면 이 때는 3의 값을 받게 된다.
이를 방지하기 위해서는 쓰기 잠금을 걸어야 한다.
트랜잭션이 완료될 때까지 Select 문장이 사용되는 모든 데이터에 Shared Lock이 걸리는 계층으로, 완벽한 읽기 일관성 모드를 제공한다.
모든 작업을 하나의 트랜잭션에서 처리하는 것과 같은 높은 고립 수준을 제공한다. 고립 수준이 높은 만큼, 동시성 처리 효율은 떨어진다.
다른 사용자는 트랜잭션 영역에 해당되는 데이터에 대한 수정이나 입력이 불가능하다.
Phantom Read가 발생하지 않지만 데이터베이스에서 거의 사용되지 않는다.
레벨이 높아질수록 트랜잭션 간 고립 정도는 높아지지만 성능은 떨어진다. 일반적인 서비스에서는 Read Committed(Oracle)나 Repeatable Read(Mysql) 중 하나를 사용한다.
트랜잭션 격리수준은 동시성과 데이터 무결성에 관련되어 있다.
동시성을 증가시키면 데이터 무결성에 문제가 발생하고, 데이터 무결성을 유지하면 동시성이 떨어지게 된다.
데이터베이스는 다수의 사용자들이 동시에 접근하는 경우가 빈번하게 발생하는데, 이 때 사용자들에 대한 적절한 통제가 이루어지지 않으면 데이터베이스의 무결성이 깨지고 트랜잭션 수행에 대해 의도하지 않은 결과가 반환될 수 있다.
DBMS는 동시성 제어 기능을 제공하여 데이터베이스의 무결성을 보호하고, 트랜잭션이 항상 정확하고 일관된 데이터를 참조할 수 있도록 한다.
데이터베이스에서 동시성은 트랜잭션을 구성하는 각각의 쿼리문들이 트랜잭션의 순서에 상관없이 동시에 실행되는 것을 말한다. 반대의 의미로 직렬성은 각각의 트랜잭션이 일정한 순서를 가지고 순차적으로 실행되는 것을 말한다.
데이터 무결성(Integrity)은 데이터의 정확성, 일관성, 유효성이 유지되는 것을 말한다.
- 개체 무결성: 모든 테이블은 기본키를 가져야 한다. 기본키는 null값이나 중복값을 가질 수 없다.
- 참조 무결성: 참조 관계에 있는 두 테이블의 데이터는 항상 일관된 값을 가지도록 한다. 참조 대상이 존재하지 않는 외래 키를 허용하지 않는다.
- Restricted: 레코드를 참조하고 있는 개체가 있으면 변경이나 삭제 연산을 취소
- Cascade: 레코드를 참조하고 있는 개체도 변경 또는 삭제함
- Set Null: 레코드를 참조하고 있는 개체의 값을 null로 설정
- 도메인 무결성: 특정 속성의 값이 그 속성이 정의된 도메인에 속한 값이어야 한다. (성별 속성의 도메인은 '남'과 '여'로 그 외의 값은 입력할 수 없다)
- 무결성 규칙: 데이터의 무결성을 지키기 위한 모든 제약 사항으로, 데이터베이스 전체에 공통적으로 적용되는 규칙