Spring Boot / HikariCP @Transactional不会覆盖隔离级别

问题描述 投票:4回答:2

我的应用程序正在我们的SQL Server数据库中的一个流量很大的表上遇到锁定争用。我们的DBA团队建议我遵循其他团队的配置,这些配置的默认事务隔离级别设置为READ_UNCOMMITTED。然后,他们应该将隔离级别设置回READ_COMMITTED以进行插入和更新。我已经反对这样做了一段时间,因为它感觉像是一个警察,而且我已经看到整个地方的警告反对使用READ_UNCOMMITTED。但是,我的双手现在被束缚了。

我正在使用Spring Boot,使用HikariCP并使用Spring Data存储库与我的SQL Server数据库进行交互。我允许Spring从我的application.properties自动配置我的DataSource,并且几乎没有其他配置。

我已经设法在我的app属性中设置我的默认事务隔离级别如下:

spring.datasource.hikari.transaction-isolation=TRANSACTION_READ_UNCOMMITTED

我已经能够通过查询事务日志,从事务条目中获取SPID并运行以下查询来验证这是否有效,现在返回“ReadUncommitted”:

SELECT CASE transaction_isolation_level 
WHEN 0 THEN 'Unspecified' 
WHEN 1 THEN 'ReadUncommitted' 
WHEN 2 THEN 'ReadCommitted' 
WHEN 3 THEN 'Repeatable' 
WHEN 4 THEN 'Serializable' 
WHEN 5 THEN 'Snapshot' END AS TRANSACTION_ISOLATION_LEVEL 
FROM sys.dm_exec_sessions 
where session_id = @@SPID

但是,在我的一个服务中,我试图将隔离级别覆盖回READ_COMMITTED,但它没有生效。

鉴于以下内容:

application.properties中的选择

spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.transaction-isolation=TRANSACTION_READ_UNCOMMITTED

JP A config.Java

@Configuration
@EnableJpaRepositories("my.project.repository")
@EntityScan(basePackages = "my.project.model")
@EnableTransactionManagement
public class JpaConfig {
    //DataSource configured by Spring from application.properties
}

my service.Java

@Service
public class MyService {

    @Autowired private MyRepository myRepository;

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void myMethod() {
        //Logic and call to myRepository.save()
    }
}

my repository.Java

public interface MyRepository extends JpaRepository<MyClass, Long> {

}

我错过了什么?我没有自定义的TransactionManager,因为我允许@EnableTransactionManagement为我配置,因为我没有发现任何地方我应该提供自己的自定义实现到目前为止。

我已经验证了如果抛出异常,事务回滚正常发生,但我无法弄清楚为什么@Transactional注释不会像我期望的那样覆盖隔离级别。

对于它的价值,我们试图解决的根本问题是我们的SQL Server数据库上的锁争用。据我所知,在SQL Server中,即使是SELECT也会锁定表(或行?)。 DBA的第一个建议是在我的查询中添加WITH(NOLOCK)提示。我无法弄清楚我的生活如何干净地完成这项工作而不必完全废弃JPA并使用本机查询。因此,他们的解决方案是默认使用READ_UNCOMMITTED,在写入事务上显式设置READ_COMMITTED。

sql-server spring-boot transactions spring-data hikaricp
2个回答
0
投票

transactionIsolation

此属性控制从池返回的连接的默认事务隔离级别。如果未指定此属性,则使用JDBC驱动程序定义的缺省事务隔离级别。如果您具有所有查询通用的特定隔离要求,则仅使用此属性。此属性的值是Connection类的常量名称,例如TRANSACTION_READ_COMMITTED,TRANSACTION_REPEATABLE_READ等。默认值:驱动程序默认值

ref:https://github.com/brettwooldridge/HikariCP


0
投票
from the source code of hikari  

    final int level = Integer.parseInt(transactionIsolationName);
            switch (level) {
               case Connection.TRANSACTION_READ_UNCOMMITTED:
               case Connection.TRANSACTION_READ_COMMITTED:
               case Connection.TRANSACTION_REPEATABLE_READ:
               case Connection.TRANSACTION_SERIALIZABLE:
               case Connection.TRANSACTION_NONE:
               case SQL_SERVER_SNAPSHOT_ISOLATION_LEVEL: // a specific isolation level for SQL server only
                  return level;
               default:
                  throw new IllegalArgumentException();
             }

As you see above you have to give numeric value of transaction level like 

spring.datasource.hikari.transaction-isolation=1 

All level numeric values listed:

TRANSACTION_NONE             = 0;
TRANSACTION_READ_UNCOMMITTED = 1;
TRANSACTION_READ_COMMITTED   = 2;
TRANSACTION_REPEATABLE_READ  = 4;
TRANSACTION_SERIALIZABLE     = 8;
SQL_SERVER_SNAPSHOT_ISOLATION_LEVEl =4096;
© www.soinside.com 2019 - 2024. All rights reserved.