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