-
[PostgreSQL] transaction isolationDB/PostgreSQL 2017. 1. 31. 10:00
SQL 표준은 transaction isolation에 대해 네 가지 레벨로 정의하고 있습니다. 이 네 가지중 가장 엄격한 것은 Serializable이며, 이것은 마치 여러 세션의 같은 트랜잭션 작업을 한 줄로 세워 차례 대로 진행하는 것과 같은 결과를 보장합니다. 나머지 세 가지 레벨은 동시에 진행되는 트랜잭션들 사이 간 허용되는 작업의 범위에 따라서 구분됩니다. 표준안에서는 Serializable이 가능한 transaction isolation 수준은 동시에 진행되는 트랜잭션 사이의 상호 관계가 전혀 없어야 한다고 정의합니다. (실 세계에서는 당연히 동시에 여러 트랜잭션들이 발생할 것이고, 이것들이 어떻게 서로 상호 관계를 안 할 수 있을까? 라는 의문점을 남깁니다.)
각 수준별 이름과 상호 작용 범위는 다음과 같습니다:
dirty read
한 트랜잭션은 다른 트랜잭션에 아직 커밋하지 않은 자료도 읽을 수 있습니다.
nonrepeatable read
한 트랜잭션은 다른 트랜잭션에서 커밋한 자료를 읽을 수 있습니다. (처음 어떤 자료를 읽고, 다시 읽으려고 하는데, 그 사이 다른 트랜잭션이 자료를 변경하고 커밋했다면, 다음 읽는 값이 커밋된 값으로 읽을 수 있습니다.)
phantom read
위와 같은 상황에서 다른 트랜잭션에 의한 커밋된 자료가 있다 하더라도, 항상 자신의 트랜잭션에서 조회 했던 그 자료값 그대로 보여줍니다.
serialization anomaly
트랜잭션 그룹을 성공적으로 커밋 한 결과는 한 번에 하나씩 모든 트랜잭션을 실행할 수 있는 순서와 일치하지 않습니다.
아래는 SQL 표준과 PostgreSQL에서 구현한 트랜잭션 isolation level을 나타낸 표입니다.
Isolation level Dirty Read Nonrepeatable Read Phantom Read Serialization Anomaly Read uncommitted 허용, PG에서는 없음 가능 가능 가능 Read committed 불가능 가능 가능 가능 Repeatable read 불가능 불가능 허용, PG에서는 없음 가능 Serializable 불가능 불가능 불가능 불가능 PostgreSQL에서는 네 가지 표준 transaction isolation level 중 하나를 요청할 수 있지만 내부적으로 세 가지 별도의 isolation level만 구현됩니다. 즉, PostgreSQL의 Read Uncommitted 모드는 커밋이 된 자료만 읽기가 가능합니다. 이는 PostgreSQL의 다중 버전 동시성 제어 아키텍처를 표준 isolation level에 매핑하는 유일한 방법이기 때문입니다.
또한 위의 표는 PostgreSQL의 Repeatable read 구현이 phantom reads를 허용하지 않음을 보여줍니다. 더 엄격한 동작은 SQL 표준에 의해 허용됩니다. 네 가지 isolaction level은 어떤 현상이 발생해서는 안되고 어떤 현상이 발생해야 하는지를 정의합니다. 사용가능한 isolation level의 동작은 아래에서 자세히 설명하겠습니다.
만약 트랜잭션 isolation level을 지정하려면 SET TRANSACTION 명령을 사용하면 됩니다.
- PostgreSQL의 몇몇 자료형과 함수는 트랜잭션 내 특별한 형태로 읽기 특성을 제공합니다. 특히 자동 증가 컬럼으로 사용하는 serial 자료형과 sequence같은 객체는 rollback이 없으며, 자료 변경 즉시 다른 모든 세션에서도 그 변경된 값을 볼 수 있습니다.
PostgreSQL에서의 트랜잭션 레벨
Read Committed Isolation Level
PostgreSQL에서의 기본 레벨임.
SELECT 쿼리
- 쿼리가 시작했을 당시의 커밋된 데이터만 참조 가능
- 쿼리가 실행되는 도중 커밋되지 않은 데이터 혹은 동시에 실행되고 있던 트랜잭션에서 커밋된 변경들은 참조하지 않음
- 외부 트랜잭션이 아닌 자신의 트랜잭션 도중에 일어난 커밋되지 않은 수정 사항은 참조할 수 있음
- 트랜잭션 내부에서 select가 여러번 있을 때, 각 select 쿼리 사이에 외부 트랜잭션이 커밋을 했으면 커밋된 다음에 실행된 select 쿼리는 커밋된 데이터를 참조하게 됨
UPDATE, DELETE, SELECT FOR UPDATE, SELECT FOR SHARE 쿼리
- select 쿼리와 동일하게 동작
- 단지 데이터를 변경하는 도중 다른 트랜잭션이 같은 로우에 접근하면 그 트랜잭션이 끝난 후에 변경사항에 대해서 작업을 진행하게 됨. 이 때, 외부 트랜잭션 때문에 변경된 로우가 현재 명령의 where 조건에 맞는지 다시 평가를 하게 됨
Read Committed는 복잡한 검색조건이 있는 상황에 맞지 않고, 은행 잔고 변경과 같은 단순한 경우에 적합.
Repeatable Read Isolaction Level
SELECT 쿼리
- 현재 트랜잭션이 시작하기전에 커밋된 데이터만 참조
- 외부 트랜잭션에서 커밋되거나 커밋되지 않은 변경을 참조하지 않음
- 외부 트랜잭션이 아닌 자신의 트랜잭션 도중에 일어난 커밋되지 않은 수정 사항은 참조할 수 있음
UPDATE, DELETE, SELECT FOR UPDATE, SELECT FOR SHARE 쿼리
- 데이터를 변경하는 도중 다른 트랜잭션이 같은 로우에 접근하면 그 트랜잭션이 끝난 후에 변경하려던 로우가 변경됐는지 확인해서 변경사항이 없으면 원래 진행하려던 명령을 실행하고, 변경됐으면 다음과 같은 에러메세지를 내면서 트랜잭션을 롤백 (ERROR: could not serialize access due to concurrent update)
- 읽기만 하는 트랜잭션에서는 에러가 없음
Read Committed와 차이점은 현재 트랜잭션에 여러번의 쿼리가 있을 경우, 현재 트랜잭션의 쿼리 중간에 다른 트랜잭션에서 변경이 있었더라도 참조하지 않고, 현재 트랜잭션이 시작되었을 때의 데이터와 현재 트랜잭션이 수행되던 도중에 일어난 변경사항만 참조
이 레벨을 사용하면 실패했을 때 재시도하는 것을 어플리케이션에서 처리해야함
Serializable Isolation Level
가장 엄격한 트랜잭션 레벨
- 트랜잭션들이 동시에 일어나지 않고, 하나씩 순서대로 실행되는 것처럼 작동
- Repeatable Read처럼 어플리케이션에서 실패했을 때 재시도 처리를 해줘야함
- Repeatable Read와 동일하게 작동하면서 트랜잭션들이 순서대로 실행중인지 모니터링
- 잘 고려하여 사용하지 않으면 성능저하 이슈가 존재
참조문서:
https://www.postgresql.org/docs/current/static/transaction-iso.html