有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

SpringJDBC中可序列化的java隔离级别

也许有人能帮我解决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”同时执行,并到达注释“comparison”(方法insertObject)。然后,它们都可以插入一个新对象,并提交它们的事务

为什么我没有回滚异常?据我所知,这就是序列化隔离级别的用途。回到上一个场景,如果x插入了一个新对象并提交了它的事务,那么“y”的事务不应该被允许提交,因为有一个新对象他没有读

也就是说,如果“y”可以再次读取secondDao的值。GetNumberOfExistingObjects(条形图)它会意识到还有一个新对象。幻影

事务配置似乎工作正常:

  • 对于每个请求,我可以看到firstDao和secondDao的相同连接
  • 每次调用insertObject时都会创建一个事务

第一个和第二个DAO如下所示:

@Autowired
public void setDataSource(DataSource dataSource) {
    this.jdbcTemplate = new JdbcTemplate(dataSource);
}

@Override
public Object daoMethod(Object param) {

        //uses jdbcTemplate

}

我肯定我错过了什么。有什么想法吗

谢谢你抽出时间

哈维尔


共 (1) 个答案

  1. # 1 楼答案

    TL;DR:在Pg 9.1中,串行化冲突的检测得到了极大的改进,所以升级


    从您的描述中很难弄清楚实际的SQL是什么,以及您希望得到回滚的原因。看起来您严重误解了可序列化隔离,可能认为它完美地测试了所有谓词,但事实并非如此,尤其是在第8.4页中

    SERIALIZABLE并不能完全保证事务的执行就好像它们是串行运行的一样——因为从性能的角度来看,如果可能的话,这样做的代价会高得令人望而却步。它只提供有限的检查。具体检查的内容以及数据库和版本之间的差异,因此您需要阅读数据库版本的文档

    异常情况是可能的,在SERIALIZABLE模式下执行的两个事务产生不同的结果,如果这些事务真正以串联方式执行

    阅读Pg中有关事务隔离的文档以了解更多信息。请注意SERIALIZABLE在第9.1页中大大改变了行为,因此请务必阅读适合您的第9.1页版本的手册Here's the 8.4 version。特别是读13.2.2.1. Serializable Isolation versus True Serializability。现在将其与大大改进的基于谓词锁定的序列化支持described in the Pg 9.1 docs进行比较

    看起来您正在尝试执行类似以下伪代码的逻辑:

    count = query("SELECT count(*) FROM the_table");
    if (count < threshold):
        query("INSERT INTO the_table (...) VALUES (...)");
    

    如果是这样的话,那么在第8.4页并行执行时,这将不起作用-它与上面链接的文档中使用的异常示例几乎相同。令人惊讶的是,它实际上在第9.1页上起作用;我甚至没有料到9.1的谓词锁定能够捕获聚合的使用

    你写道:

    Coming back to the previous scenario, if x manages to insert a new object and commits its transaction, then "y"'s transaction should not be allowed to commit since there is a new object he did not read.

    但是8.4不会检测到这两个事务是相互依赖的,您可以通过使用两个psql会话来测试它来证明这一点。只有在9.1中引入了真正的可序列化性的东西之后,这才会起作用——坦率地说,我很惊讶它在9.1中能起作用

    如果您想在第8.4页中强制执行最大行数,则需要^{} the table来防止并发INSERT,手动或通过trigger function执行锁定。在触发器中执行此操作本质上需要锁升级,因此经常会出现死锁,但会成功完成此工作。最好在应用程序中完成,在从表中获取SELECT之前可以发出LOCK TABLE my_table IN 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;
    

    在第9.1页上运行时,st commits succeeds then the second提交`失败,原因是:

    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中添加所有用于序列化的谓词锁定代码