在应用程序代码(如@Transactional)中设置隔离级别与在db服务器上配置的隔离级别之间有什么关系?

问题描述 投票:0回答:1

我将所有信息拼凑在一起非常困难。因此,让我尽可能详细地解释。

在spring-jdbc,spring-transaction,MySQL db应用程序上工作,我希望能够在选择更新时锁定表中的数据行。原因是,我有在不同阶段转换数据的业务逻辑。因此,如果应用程序实例(在服务器1上)拾取记录进行处理,则不应被另一个实例拾取。这有点类似于this question。但是由于OP对该问题的相同原因,我无法接受答案。

因此,在仔细检查了spring-transaction reference doc并学习了如何使用jdbcTemplate配置事务管理之后,我的spring应用程序设置如下(仅相关部分):

applicationContext.xml

...
...
<context:annotation-config />
...
<context:component-scan base-package="org.foo.bar"/>
...
<!-- Enable Annotation based Declarative Transaction Management -->
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />
<bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>
...
<bean id="dataSource"
      class="org.apache.commons.dbcp2.BasicDataSource"  destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://dbserver:3306" />
    <property name="username" value="foo" />
    <property name="password" value="baz" />
    <property name="initialSize" value="10" />
    <property name="maxTotal" value="20"/>
</bean>

带有目标方法的服务类:TransactionSvcImpl.java

public class TransactionSvcImpl implements TransactionSvc {
    @Autowired
    DatabaseAccessService dbAccessService;

    @Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRES_NEW)
    @Override
    public List<Foo> getFooForUpdate() {
        // Below call executes a SQL like - "SELECT * FROM some_t WHERE id = 1 FOR UPDATE"
        List<foo> foos = dbAccessService.getSomeRecord();
        dbAccessService.updateTheRecord(foos, Status.PROCESSING);
        return foos;
    }
}

现在,我按照this answer中的描述测试了隔离级别在MySQL中的工作方式。但是,要使“选择更新”起作用,我必须在数据库服务器本身上更改以下属性:

SET tx_isolation = 'SERIALIZABLE';
SET AUTOCOMMIT=0;

没有更改这些设置,我无法使SELECT ... FOR UPDATE工作。

一旦我手动测试它,我想看看当我的应用程序代码运行时这是否有任何作用。因此,当我保持mysql命令行会话之一处于活动状态时,我启动了我的应用程序,并在dbAccessService.updateTheRecord();行放置了一个断点。然后,在另一个会话中,我尝试选择此行(无select for update,只有一个简单的select)语句。现在,我可以看到该行已被锁定,因为该行是由我的应用程序代码启动的先前的事务。

问题:

  1. 要通过spring-transaction实现行锁定(SELECT ... FOR UPDATE行为),是否必须更改数据库服务器上的设置?像Isolation_level和自动提交?
  2. 假设MySQL服务器在默认值下运行(ISOLATION_LEVEL = REPEATABLE-READS,AUTOCOMMIT = 1)。现在这仍然有效吗?意思是,如果像我在上面那样在目标方法上在spring上配置事务,一个应用程序实例正在读取的行是否将保持锁定状态,以便其他试图读取该行的实例将被阻止?
  3. 我们在@Transactional批注上添加的配置如何使其(或影响)数据库?我知道我们可以为MySQL documentation here的表13.9中所述的不同范围设置事务特征。基本上,以下是我的想象力或对所有这些工作原理的猜测。正确吗?

全部如何运作?

  • 用@Transactional注释的类/方法将导致创建代理对象,以便将所有事务性内容与用户实现的业务逻辑一起添加。
  • 已获取数据库连接并创建了数据库会话。
  • 对于此会话,基于@Transactional中提供的配置,设置了适当的隔离级别(不知道自动提交将受到怎样的影响)
  • 交易完成后,会话关闭。创建的任何新会话都将相应地设置隔离特征。

更新:我不一定要了解spring框架。采用任何支持db API并提供事务管理的应用程序开发框架(例如Django)。为提供诸如功能之类的元数据,需要考虑哪些部分(诸如jdbc之类的db驱动程序,一些标准接口?)?他们怎么做到的?

  1. 使用spring-jdbc和spring-tx,我是否有更好的方法来实现所需的SELECT ... FOR UPDATE功能?我可能会介绍自己的锁定机制,例如,引入新的布尔列,例如selected_for_processing,而不是选择,而是通过设置selected_for_processing列来更新要选择的记录。在下一步中,我将基于此标志和其他状态条件进行选择。因此,这种记录将只被处理一次。但是我想知道是否有常规或配置的方式来实现这一目标?我不能使用JPA或任何spring-data-x库。 spring-data-library适用于springframework 5.x.x或更高版本,但我受制于springframework 4.2.3,因此spring-jdbc,spring-tx。
java mysql spring-jdbc spring-transactions transaction-isolation
1个回答
0
投票

有所作为的是自动提交。通常,自动提交为ON(每个查询本身就是一个事务),要使用事务,您需要在代码中使用BEGINSTART TRANSACTION,在事务中执行所需的操作,然后显式调用COMMIT

1)服务器上的transaction_isolation设置将更改服务器上的默认设置。但是您可以在开始事务之前在本地会话中更改它们,而无需更改服务器范围的默认值。

2)是的,不同会话之间的tx_isolation可能不同。

3)不确定我可以完全回答这个问题,因为它似乎是特定于Spring的,并且我不太流利。但是,总的来说,Spring没有真正的方法来确保或实现事务性以及在不显式锁定整个表的情况下锁定数据库级别,因此最终,它所能做的就是使用基础数据库功能,并试图变得比基础数据库随附的更聪明。巨大的性能和并发代价。

© www.soinside.com 2019 - 2024. All rights reserved.