【问题标题】:Concurrency - Spring + Hibernate + SQL Server并发 - Spring + Hibernate + SQL Server
【发布时间】:2021-10-31 00:21:24
【问题描述】:

即使在spring事务中使用了可序列化的隔离级别后,我也遇到了并发问题。我的用例是用户将以以下格式提供要在数据库中更新的配置。

{A1: [B1, B2, B3]}

我必须将它保存在下面的实体中。

A {
    @OneToMany
    List<B> bList;
}

B {
    @ManyToOne
    A a;
    
    Boolean isDeleted;
}

当有保存配置的并发请求时,插入的 B 比预期的要多。请参考以下场景。

Initial enitites in database: A1 -> []

Transaction 1 - given config {A1: [B2]}

Reads A1 -> []
Insert B2

Transaction 2 - given config {A1: [B3]}

Reads A1 -> []
Insert B3

Final in database: A1 -> [B2, B3] when expected is either A1 -> [B2, B3-deleted] or A1 -> [B2-deleted, B3].

即使经过大量研究,我也无法找到解决此问题的适当方法。 根据这篇文章 (https://sqlperformance.com/2014/04/t-sql-queries/the-serializable-isolation-level),当使用 SQL Server 时,这种情况总是可能发生的,因为操作顺序是有效的序列化之一。

【问题讨论】:

    标签: sql-server hibernate concurrency spring-data


    【解决方案1】:

    最好通过引入用于乐观锁定的版本列来处理。不需要使用 SERIALIZABLE 隔离级别。只需使用

    A {
        @Version
        long version;
        @OneToMany
        List<B> bList;
    }
    

    并确保在加载A 时使用LockModeType.OPTIMISTIC_FORCE_INCREMENT。这样,“序列化”将基于您所谓的“聚合根”的锁,即A

    通过这样做,一个事务将成功而另一个事务将失败,因为在每个事务结束时,只有在此期间值没有更改时,版本列才会增加。如果同时发生变化,它将回滚两个事务之一,您将看到一个 OptimisticLockException。

    【讨论】:

    • 谢谢,@Christian。我以同样的方式处理了这个问题,但使用了LockModeType.PESSIMISTIC_FORCE_INCREMENT
    猜你喜欢
    • 2016-08-12
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 2010-10-12
    • 2014-12-01
    • 1970-01-01
    • 2011-09-26
    • 1970-01-01
    相关资源
    最近更新 更多