복붙노트

[SPRING] Spring-JDBC에서 격리 수준 SERIALIZABLE

SPRING

Spring-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. ==============================

    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에 대한 모든 술어 잠금 코드가 없었기 때문입니다.

  2. from https://stackoverflow.com/questions/12824217/isolation-level-serializable-in-spring-jdbc by cc-by-sa and MIT license