Mat와 Erwin은 둘 다 맞으며 의견에 맞지 않는 방식으로 그들이 말한 것을 더 확장하기 위해 또 다른 대답을 추가하고 있습니다. 그들의 답변이 모든 사람을 만족시키는 것은 아니며, PostgreSQL 개발자와 상담해야한다는 제안이 있었으며, 저는 하나입니다.
여기서 중요한 점은 SQL 표준에 따라 READ COMMITTED
트랜잭션 격리 수준 에서 실행되는 트랜잭션 내에서 커밋되지 않은 트랜잭션 작업을 볼 수 없다는 제한이 있습니다. 때 커밋 된 트랜잭션의 작업이 표시됩니다 구현에 의존합니다. 당신이 지적하는 것은 두 제품이 그것을 구현하기 위해 선택한 방법의 차이입니다. 두 가지 구현 모두 표준 요구 사항을 위반하지 않습니다.
PostgreSQL에서 발생하는 세부 사항은 다음과 같습니다.
S1-1이 실행 됨 (1 행 삭제)
S1이 여전히 롤백 될 수 있기 때문에 이전 행은 그대로 남아 있지만 S1은 이제 행에 대한 잠금을 유지하므로 행을 수정하려는 다른 세션이 S1이 커밋 또는 롤백되는지를 기다립니다. 모든이 읽고 그들이 그것을 잠금을 시도하지 않는 한 여전히 이전 행을 볼 수있는 테이블의 SELECT FOR UPDATE
나 SELECT FOR SHARE
.
S2-1이 실행되지만 S1에 쓰기 잠금이 있으므로 차단됩니다.
S2는 이제 S1의 결과를보기 위해 기다려야합니다. S1이 커밋이 아닌 롤백 인 경우 S2는 행을 삭제합니다. 롤백하기 전에 S1이 새 버전을 삽입 한 경우 새 버전이 다른 트랜잭션의 관점에서 존재하지 않았거나 이전 버전이 다른 트랜잭션의 관점에서 삭제되지 않았을 것입니다.
S1-2 런 (1 열 삽입)
이 행은 이전 행과 독립적입니다. id = 1 인 행이 업데이트 된 경우 이전 버전과 새 버전이 관련되며 S2는 행이 차단되지 않은 경우 업데이트 된 버전의 행을 삭제할 수 있습니다. 새 행은 과거에 존재했던 일부 행과 동일한 값을 가지므로 해당 행의 업데이트 버전과 동일하지 않습니다.
S1-3이 실행되고 쓰기 잠금이 해제됩니다
따라서 S1의 변경 사항이 유지됩니다. 한 행이 사라졌습니다. 한 행이 추가되었습니다.
S2-1이 작동하여 잠금을 얻을 수 있습니다. 그러나 보고서 0 행이 삭제되었습니다. 허 ???
내부적으로 일어나는 것은 행의 한 버전에서 동일한 행의 다음 버전으로 포인터가 업데이트된다는 것입니다. 행이 삭제되면 다음 버전이 없습니다. READ COMMITTED
쓰기 충돌시 블록에서 트랜잭션이 깨어날 때 해당 업데이트 체인을 따라 끝까지 이어집니다. 행이 삭제되지 않았고 여전히 쿼리의 선택 기준을 충족하는 경우 처리됩니다. 이 행이 삭제되었으므로 S2의 쿼리가 계속 진행됩니다.
S2는 테이블 스캔 중에 새 행에 도달 할 수도 있고 그렇지 않을 수도 있습니다. 그렇다면 S2의 DELETE
명령문이 시작된 후 새 행이 작성 되어 행 세트의 일부가 아닌 것을 볼 수 있습니다.
PostgreSQL이 S2의 전체 DELETE 문을 처음부터 새 스냅 샷으로 다시 시작하면 SQL Server와 동일하게 작동합니다. PostgreSQL 커뮤니티는 성능상의 이유로 그렇게하지 않았습니다. 이 간단한 경우 성능의 차이를 결코 눈치 채지 못할 것입니다. 그러나 DELETE
막혔을 때 천만 개의 행이 있다면 확실히 그럴 것입니다. 더 빠른 버전은 여전히 표준의 요구 사항을 준수하기 때문에 PostgreSQL이 성능을 선택한 위치에 상충 관계가 있습니다.
S2-2 실행, 고유 키 제약 조건 위반보고
물론 행이 이미 존재합니다. 이것은 그림에서 가장 놀라운 부분입니다.
여기에는 놀라운 동작이 있지만 모든 것은 SQL 표준을 따르며 표준에 따라 "구현 별"의 범위 내에 있습니다. 다른 구현의 동작이 모든 구현에 존재한다고 가정하면 PostgreSQL은 READ COMMITTED
격리 수준 에서 직렬화 실패를 피하기 위해 매우 열심히 노력 하고 있으며이를 달성하기 위해 다른 제품과 다른 일부 동작을 허용합니다.
개인적으로 저는 모든 제품 구현 READ COMMITTED
에서 트랜잭션 격리 수준을 좋아하지 않습니다 . 그들은 모두 경쟁 조건이 거래 관점에서 놀라운 행동을하도록 허용합니다. 한 제품이 허용하는 이상한 동작에 익숙해지면 "정상"과 다른 제품이 선택한 절충을 고려하는 경향이 있습니다. 그러나 모든 제품은 실제로로 구현되지 않은 모드에 대해 일종의 절충점을 만들어야 합니다. PostgreSQL 개발자가 줄을 서기로 선택한 곳에서는 차단을 최소화하고 (읽기가 쓰기를 차단하지 않고 쓰기가 읽기를 차단하지 않음) 직렬화 실패 가능성을 최소화합니다.SERIALIZABLE
READ COMMITTED
표준은 SERIALIZABLE
트랜잭션을 기본값으로 요구 하지만, 대부분의 제품은 더 느슨한 트랜잭션 격리 수준보다 성능이 저하되기 때문에 그렇게하지 않습니다. 일부 제품 SERIALIZABLE
은 선택 시 실제로 직렬화 가능한 트랜잭션을 제공하지 않으며, 특히 Oracle 및 9.1 이전의 PostgreSQL 버전입니다. 그러나 SERIALIZABLE
실제 거래를 사용하는 것은 경쟁 조건으로 인한 놀라운 영향을 피할 수있는 유일한 방법이며, 경쟁 조건을 피하기 위해 SERIALIZABLE
트랜잭션을 항상 차단하거나 경쟁 조건이 발생하지 않도록 일부 트랜잭션을 롤백해야합니다. SERIALIZABLE
트랜잭션 의 가장 일반적인 구현은 S2PL (Strict Two-Phase Locking)이며 차단 및 직렬화 실패 (교착 상태 형태)를 모두 가지고 있습니다.
전체 공개 : 나는 MIT의 Dan Ports와 함께 Serializable Snapshot Isolation이라는 새로운 기술을 사용하여 PostgreSQL 버전 9.1에 진정으로 직렬화 가능한 트랜잭션을 추가했습니다.