标签:java postgresql spring transactions spring-transactions
也许有人可以帮我解决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级别的用途.回到上一个场景,如果x设法插入一个新对象并提交其事务,则不应该允许“y”的事务提交,因为有一个他没有读过的新对象.
也就是说,如果“y”可以再次读取secondDao.getNumerOfExistingObjects(bar)的值,它将意识到有更多的新对象.幻影?
事务配置似乎工作正常:
>对于每个请求,我可以看到firstDao和secondDao的相同连接
>每次调用insertObject时都会创建一个事务
第一个和第二个DAO如下:
@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Object daoMethod(Object param) {
//uses jdbcTemplate
}
我相信我错过了一些东西.任何的想法?
谢谢你的时间,
哈维尔
解决方法:
TL; DR:Pg 9.1中可检测性的可串行性冲突得到了显着改善,因此升级.
从您的描述中弄清楚实际的SQL是什么以及您期望获得回滚的原因是很棘手的.看起来你已经严重误解了可序列化的隔离,或许认为它完美地测试了所有谓词,但它没有,特别是在Pg 8.4中.
SERIALIZABLE并不能完美地保证事务的执行就好像它们是串行运行一样 – 如果它完全可能的话,从性能的角度来看这样做会非常昂贵.它只提供有限的检查.确切地说,检查的内容以及数据库与数据库和版本之间的差异如何,因此您需要阅读适用于您的数据库版本的文档.
异常是可能的,其中两个以SERIALIZABLE模式执行的事务产生不同的结果,如果这些事务真正串行执行.
阅读Pg中有关事务隔离的文档以了解更多信息.请注意,SERIALIZABLE在Pg 9.1中显着改变了行为,因此请务必阅读适合您的Pg版本的手册版本. 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 (...)");
如果是这样,那么当并发执行时,它不会在Pg 8.4中起作用 – 它与上面链接的文档中使用的异常示例几乎相同.令人惊讶的是它实际上适用于Pg 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中工作.
如果你想在Pg 8.4中强制执行最大行数,你需要LOCK
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;
当在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中添加可序列化的所有谓词锁定代码.
标签:java,postgresql,spring,transactions,spring-transactions 来源: https://codeday.me/bug/20190613/1231547.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。