【问题标题】:Handling concurrent transaction in Spring-Boot在 Spring-Boot 中处理并发事务
【发布时间】:2019-04-28 02:14:38
【问题描述】:

所以,这似乎是一个非常菜鸟的问题,但经过一番谷歌搜索后,我找不到最好的方法。

问题:有一个 Spring-Boot 应用程序,与数据库 (MySQL) 交互。 考虑一个表 T1,我不希望每个用户只有一个有效条目。 因此,如果出现下一个条目,它会软删除(通过设置 deleted-at 列)该用户的现有行并创建一个新条目。 现在问题来了,几乎同时有多个请求。

我的事务块,看起来像这样:

@Transactional
someMethod() {
    CheckIfAnyValidEntryForUser(U1);    
    SoftDeleteExistingEntries(U1);
    DoComputation();   // Takes around a second.
    InsertNewRow();
}

现在,如果在短时间内有多个请求,那么我的代码最终会为单个用户插入多个有效条目。

由于我要编辑的行以前可能不存在,所以我不能按版本号进行乐观锁定。

正在考虑在这个 someMethod 上获取全局锁定,但这会增加我的延迟太多。

另一种方法可能是在数据库中为列(包括 delete-at 列)使用复合唯一键。然后在代码中处理提交失败。

这似乎是一个很常见的问题,那么通常如何处理呢? 最好的方法是什么?

【问题讨论】:

    标签: java mysql spring spring-boot spring-transactions


    【解决方案1】:

    您可以使用@Transactional注解的isolation属性,并将该属性设置为Isolation.SERIALIZABLE:

    @Transactional(isolation= Isolation.SERIALIZABLE)
    

    请注意:

    Spring 默认使用 DEFAULT 隔离策略,该策略使用底层数据存储的默认隔离级别。使用 SERIALIZABLE 隔离使您的事务在读、写、范围锁定的情况下执行;所以他们看起来好像被处决了 以序列化的方式,防止脏读、不可重复读和 幻读。

    还有

    专为与 Propagation.REQUIRED 或 Propagation.REQUIRES_NEW 一起使用而设计,因为它仅适用于新启动的事务。如果您希望在参与具有不同隔离级别的现有事务时拒绝隔离级别声明,请考虑将事务管理器上的“validateExistingTransactions”标志切换为“true”。

    【讨论】:

    • 谢谢@Abdelghani。这解决了上述问题,但会导致另一个问题。由于死锁条件,它现在不断生成LockAcquisitionException。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-29
    • 1970-01-01
    • 2019-03-17
    • 2019-01-09
    • 1970-01-01
    • 1970-01-01
    • 2021-09-15
    相关资源
    最近更新 更多