简体   繁体   English

Spring-JDBC中的隔离级别SERIALIZABLE

[英]Isolation level SERIALIZABLE in Spring-JDBC

maybe somebody can help me with a transactional issue in Spring (3.1)/ Postgresql (8.4.11) 也许有人可以帮我解决Spring(3.1)/ Postgresql(8.4.11)中的事务问题

My transactional service is as follows: 我的交易服务如下:

@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(...) 
            }
            ....
}

The Spring configuration Webapp contains: 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());
    }
}

Let us say a request "x" and a request "y" execute concurrently and arrive both at the comment "comparison" (method insertObject). 让我们说请求“x”和请求“y”同时执行并且到达注释“比较”(方法insertObject)。 Then both of them are allowed to insert a new object and their transactions are commited. 然后允许它们都插入一个新对象并提交它们的事务。

Why am I not having a RollbackException? 为什么我没有RollbackException? As far as I know that is what the Serializable isolotation level is for. 据我所知,这是Serializable isolotation级别的用途。 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. 回到上一个场景,如果x设法插入一个新对象并提交其事务,则不应该允许“y”的事务提交,因为有一个他没有读过的新对象。

That is, if "y" could read again the value of secondDao.getNumerOfExistingObjects(bar) it would realize that there is a new object more. 也就是说,如果“y”可以再次读取secondDao.getNumerOfExistingObjects(bar)的值,它将意识到有更多的新对象。 Phantom? 幻影?

The transaction configuration seems to be working fine: 事务配置似乎工作正常:

  • For each request I can see the same connection for firstDao and secondDao 对于每个请求,我可以看到firstDao和secondDao的相同连接
  • A transaction is created everytime insertObject is invoked 每次调用insertObject时都会创建一个事务

Both first and second DAOs are as follows: 第一个和第二个DAO如下:

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

@Override
public Object daoMethod(Object param) {

        //uses jdbcTemplate

}

I am sure I am missing something. 我相信我错过了一些东西。 Any idea? 任何的想法?

Thanks for your time, 谢谢你的时间,

Javier 哈维尔

TL;DR: Detection of serializability conflicts improved dramatically in Pg 9.1, so upgrade. TL; DR:Pg 9.1中可检测性的可串行性冲突得到了显着改善,因此升级。


It's tricky to figure out from your description what the actual SQL is and why you expect to get a rollback. 从您的描述中弄清楚实际的SQL是什么以及您期望获得回滚的原因是很棘手的。 It looks like you've seriously misunderstood serializable isolation, perhaps thinking it perfectly tests all predicates, which it doesn't, especially not in Pg 8.4. 看起来你已经严重误解了可序列化的隔离,或许认为它完美地测试了所有谓词,但它没有,特别是在Pg 8.4中。

SERIALIZABLE doesn't perfectly guarantee that the transactions execute as if they were run in series - as doing so would be prohibitively expensive from a performance point of view if it it were possible at all. SERIALIZABLE并不能完美地保证事务的执行就好像它们是串行运行一样 - 如果它完全可能的话,从性能的角度来看这样做会非常昂贵。 It only provides limited checking. 它只提供有限的检查。 Exactly what is checked and how varies from database to database and version to version, so you need to read the docs for your version of your database. 确切地说,检查的内容以及数据库与数据库和版本之间的差异如何,因此您需要阅读适用于您的数据库版本的文档。

Anomalies are possible, where two transactions executing in SERIALIZABLE mode produce a different result to if those transactions truly executed in series. 异常是可能的,其中两个以SERIALIZABLE模式执行的事务产生不同的结果,如果这些事务真正串行执行。

Read the documentation on transaction isolation in Pg to learn more. 阅读Pg中有关事务隔离的文档以了解更多信息。 Note that SERIALIZABLE changed behaviour dramatically in Pg 9.1, so make sure to read the version of the manual appropriate for your Pg version. 请注意, SERIALIZABLE在Pg 9.1中显着改变了行为,因此请务必阅读适合您的Pg版本的手册版本。 Here's the 8.4 version . 这是8.4版本 In particular read 13.2.2.1. 特别是阅读13.2.2.1。 Serializable Isolation versus True Serializability . 可序列化隔离与真正可串行化 Now compare that to the greatly improved predicate locking based serialization support described in the Pg 9.1 docs . 现在将其与Pg 9.1文档中描述的基于谓词锁定的序列化支持进行比较。

It looks like you're trying to perform logic something like this pseudocode: 看起来你正在尝试执行类似这种伪代码的逻辑:

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

If so, that's not going to work in Pg 8.4 when executed concurrently - it's pretty much the same as the anomaly example used in the documentation linked above. 如果是这样,那么当并发执行时,它不会在Pg 8.4中起作用 - 它与上面链接的文档中使用的异常示例几乎相同。 Amazingly it actually works on Pg 9.1; 令人惊讶的是它实际上适用于Pg 9.1; I didn't expect even 9.1's predicate locking to catch use of aggregates. 我没想到甚至9.1的谓词锁定来捕获聚合的使用。

You write that: 你写的是:

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. 回到上一个场景,如果x设法插入一个新对象并提交其事务,则不应该允许“y”的事务提交,因为有一个他没有读过的新对象。

but 8.4 won't detect that the two transactions are interdependent, something you can trivially prove by using two psql sessions to test it. 但8.4不会检测到这两个事务是相互依赖的,你可以通过使用两个psql会话来测试它。 It's only with the true-serializability stuff introduced in 9.1 that this will work - and frankly, I was surprised it works in 9.1. 只有在9.1中引入的真正可串行化的东西才能实现 - 坦率地说,我很惊讶它在9.1中工作。

If you want to do something like enforce a maximum row count in Pg 8.4, you need to LOCK the table to prevent concurrent INSERT s, doing the locking either manually or via a trigger function . 如果你想在Pg 8.4中强制执行最大行数,你需要LOCK以防止并发INSERT ,手动或通过触发函数进行锁定。 Doing it in a trigger will inherently require a lock promotion and thus will frequently deadlock, but will successfully do the job. 在触发器中执行它本身就需要锁定升级,因此经常会死锁,但会成功完成工作。 It's better done in the application where you can issue the LOCK TABLE my_table IN EXCLUSIVE MODE before obtaining even SELECT ing from the table, so it already has the highest lock mode it will need on the table and thus shouldn't need deadlock-prone lock promotion. 最好在应用程序中完成,你可以在从表中获取甚至SELECT之前发出LOCK TABLE my_table IN EXCLUSIVE MODE ,因此它已经具有表所需的最高锁模式,因此不需要易死锁推广。 The EXCLUSIVE lock mode is appropriate because it permits SELECT s but nothing else. EXCLUSIVE锁定模式是合适的,因为它允许SELECT但没有别的。

Here's how to test it in two psql sessions: 以下是在两个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;

When run on Pg 9.1, the st commits succeeds then the second COMMIT` fails with: 当在Pg 9.1上运行时, st commits succeeds then the second COMMIT`失败:

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.

but when run on 8.4 both commits commits succeed, because 8.4 didn't have all the predicate locking code for serializability added in 9.1. 但是当在8.4上运行时,两个提交提交都成功,因为8.4没有在9.1中添加可序列化的所有谓词锁定代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM