[SPRING] Spring-JDBC에서 격리 수준 SERIALIZABLE
SPRINGSpring-JDBC에서 격리 수준 SERIALIZABLE
어쩌면 누군가가 Spring (3.1) / PostgreSQL (8.4.11)의 트랜잭션 문제로 나를 도울 수 있습니다.
내 트랜잭션 서비스는 다음과 같습니다.
@Transactional(isolation = Isolation.SERIALIZABLE, readOnly = false)
@Override
public Foo insertObject(Bar bar) {
// these methods are just examples
int x = firstDao.getMaxNumberOfAllowedObjects(bar)
int y = secondDao.getNumerOfExistingObjects(bar)
// comparison
if (x - y > 0){
secondDao.insertNewObject(...)
}
....
}
Spring 구성 Webapp에는 다음이 포함됩니다.
@Configuration
@EnableTransactionManagement
public class ....{
@Bean
public DataSource dataSource() {
org.apache.tomcat.jdbc.pool.DataSource ds = new DataSource();
....configuration details
return ds;
}
@Bean
public DataSourceTransactionManager txManager() {
return new DataSourceTransactionManager(dataSource());
}
}
요청 "x"와 요청 "y"가 동시에 실행되어 주석 "비교"(메소드 insertObject)에 도착한다고합시다. 그런 다음 두 객체 모두 새 객체를 삽입 할 수 있으며 트랜잭션이 커밋됩니다.
RollbackException이 발생하지 않는 이유는 무엇입니까? 내가 아는 한 Serializable isolotation level이 그 것이다. 이전 시나리오로 돌아가서 x가 새 객체를 삽입하고 트랜잭션을 커밋하면 "y"의 트랜잭션은 커밋되지 않아야합니다. 왜냐하면 그가 읽지 않은 새 객체가 있기 때문입니다.
즉, "y"가 secondDao.getNumerOfExistingObjects (bar)의 값을 다시 읽을 수 있으면 더 많은 새 객체가 있음을 알게됩니다. 환상?
트랜잭션 구성이 정상적으로 작동하는 것 같습니다.
첫 번째 및 두 번째 DAO는 모두 다음과 같습니다.
@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Object daoMethod(Object param) {
//uses jdbcTemplate
}
나는 뭔가를 놓치고 있다고 확신한다. 어떤 생각?
시간 내 줘서 고마워,
하비에르
해결법
-
==============================
1.TL : DR : 직렬화 가능성 충돌 탐지가 9.1 절에서 크게 개선되어 업그레이드되었습니다.
TL : DR : 직렬화 가능성 충돌 탐지가 9.1 절에서 크게 개선되어 업그레이드되었습니다.
실제 SQL이 무엇이고 롤백을 기대하는 이유에 대해 설명하는 것이 까다로울 수 있습니다. 직렬화 가능 격리를 심각하게 오해 한 것처럼 보입니다. 아마도 모든 조건자를 완벽하게 테스트한다고 생각할 수 있습니다. 특히 그렇지 않습니다. 특히 8.4 절은 그렇지 않습니다.
SERIALIZABLE은 트랜잭션이 직렬로 실행되는 것처럼 실행되도록 완벽하게 보장하지 않습니다. 그렇게하면 성능상의 관점에서 볼 때 너무 비쌉니다. 제한된 검사 만 제공합니다. 검사 대상은 무엇이며 데이터베이스는 데이터베이스와 버전에 따라 다르므로 데이터베이스 버전에 해당하는 문서를 읽어야합니다.
SERIALIZABLE 모드에서 실행되는 두 개의 트랜잭션이 진정으로 직렬로 실행되는 경우와 다른 결과를 생성하는 예외가 발생할 수 있습니다.
자세한 내용은 Pg의 트랜잭션 격리에 대한 설명서를 참조하십시오. SERIALIZABLE은 Pg 9.1에서 극적인 동작을 변경 했으므로, Pg 버전에 적합한 매뉴얼 버전을 읽으십시오. 여기에 8.4 버전이 있습니다. 특히 13.2.2.1을 읽으십시오. Serializable Isolation 대 True Serializability. 이제이를 Pg 9.1 문서에 설명 된 크게 향상된 술어 잠금 기반 직렬화 지원과 비교하십시오.
이 의사 코드와 같은 논리를 수행하려는 것 같습니다.
count = query("SELECT count(*) FROM the_table"); if (count < threshold): query("INSERT INTO the_table (...) VALUES (...)");
그렇다면, 동시에 실행될 때 8.4 절에서 작동하지 않을 것입니다. 위의 문서에서 사용 된 예외 예제와 거의 같습니다. 놀랍게도 Pg 9.1에서 실제로 작동합니다. 나는 집계의 사용을 잡기 위해 9.1의 술어조차도 기대하지 않았다.
당신은 그것을 씁니다 :
하지만 8.4에서는 두 트랜잭션이 상호 의존적이라는 것을 감지하지 못합니다. 두 개의 psql 세션을 사용하여 테스트 할 수 있습니다. 9.1에서 소개 된 진정한 직렬화 가능성이있는 것만으로도 작동합니다. 솔직히 말해서, 9.1에서 작동한다는 것이 놀랍습니다.
Pg 8.4에서 최대 행 수를 적용하려는 경우, 수동 INSERT 또는 트리거 기능을 통해 동시 INSERT를 방지하기 위해 테이블을 잠글 필요가 있습니다. 방아쇠를 당기면 본질적으로 잠금 승격이 필요하므로 자주 교착 상태가되지만 성공적으로 수행 할 수 있습니다. 테이블에서 SELECT를 얻기 전에 LOCK TABLE my_table을 EXCLUSIVE MODE로 발행 할 수있는 응용 프로그램에서 더 잘 수행되므로 테이블에 필요한 가장 높은 잠금 모드가 이미 있으므로 교착 상태가 발생하기 쉬운 잠금 승격이 필요하지 않습니다. . EXCLUSIVE 잠금 모드는 SELECT를 허용하지만 다른 것은 허용하지 않으므로 적절합니다.
두 가지 psql 세션에서 테스트하는 방법은 다음과 같습니다.
SESSION 1 SESSION 2 create table ser_test( x text ); BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; SELECT count(*) FROM ser_test ; SELECT count(*) FROM ser_test ; INSERT INTO ser_test(x) VALUES ('bob'); INSERT INTO ser_test(x) VALUES ('bob'); COMMIT; COMMIT;
Pg 9.1에서 st 커밋이 성공하면 secondCOMMIT`는 다음과 같이 실패합니다 :
regress=# COMMIT; ERROR: could not serialize access due to read/write dependencies among transactions DETAIL: Reason code: Canceled on identification as a pivot, during commit attempt. HINT: The transaction might succeed if retried.
하지만 8.4에서 실행될 때 커밋 커밋은 성공합니다. 왜냐하면 8.4에는 9.1에 추가 된 serializability에 대한 모든 술어 잠금 코드가 없었기 때문입니다.
from https://stackoverflow.com/questions/12824217/isolation-level-serializable-in-spring-jdbc by cc-by-sa and MIT license