当前位置: 首页 > 知识库问答 >
问题:

HiberNate乐观锁定Postgres和MariaDb之间的不同行为

杜凯
2023-03-14

我刚刚发现,当我对Postgresql或MariaDB数据库使用乐观锁定时,我的应用程序表现不同,我想知道是否有人可以解释发生了什么,以及我如何才能使应用程序以相同的方式使用MariaDB?我使用Postgresl 10.5和MariaDB 10.3.10,带有InnoDB引擎和默认设置。我用的是Spring框架5.1.0版,Hibernate 5.3.6。

所以我的代码看起来像这样:

@Entity
@Getter
@Setter
@NoArgsConstructor
public class Bla {

    @Id
    @GeneratedValue
    private long id;

    @Version
    private long version;

    private int counter;
}

我还有一个用于此实体和以下服务方法的存储库:

@Transactional
public int increment(long id) {
    Bla bla = blaRepo.getOne(id);
    bla.setCounter(bla.getCounter() + 1);
    return bla.getCounter();
}

如果我在多个线程上调用此方法,我希望更新只会对其中一个线程成功,如果他们接触具有相同版本的实体。举个例子:如果我在一次运行中使用 Postgres db 启动 50 个线程,我会收到 3 个成功的调用并返回值 1、2、3,其他 47 个失败,并带有预期行为 ObjectOptimisticLockingFailureException - 这就是我希望应用程序的行为方式。

然而,如果我切换到MariaDB,这种情况就不会发生。所有50个线程都成功完成,我在多个线程中得到相同的响应值,就好像没有乐观锁一样。例如,现在前5个线程返回1,然后其中20个返回2,其余的返回3或4。

为什么会发生这种情况?这没有任何意义——对于两个数据库,生成的查询都是

update bla set counter=?, version=? where id=? and version=?

但是在Postgreql中会正确失败,而MariaDB会意外成功。

共有2个答案

郜昊苍
2023-03-14

我找到了解决这个问题的方法。

似乎我在application.properties中设置了此属性:

spring.jpa.properties.hibernate.jdbc.batch_size = 50

当我使用 Postgresql 时,我得到以下带有两个线程的调试跟踪:

13223 [pool-2-thread-2] DEBUG org.hibernate.SQL - update bla set counter=?, version=? where id=? and version=?
13223 [pool-2-thread-1] DEBUG org.hibernate.SQL - update bla set counter=?, version=? where id=? and version=? 
13226 [pool-1-thread-1] DEBUG org.hibernate.engine.jdbc.batch.internal.BatchingBatch - Executing batch size: 1
13226 [pool-1-thread-2] DEBUG org.hibernate.engine.jdbc.batch.internal.BatchingBatch - Executing batch size: 1
13230 [pool-1-thread-1] ERROR org.hibernate.engine.jdbc.batch.internal.BatchingBatch - HHH000315: Exception executing batch [org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1], SQL: update bla set counter=?, version=? where id=? and version=?

然后使用具有相同批量大小50的MariaDB:

21978 [pool-2-thread-2] DEBUG org.hibernate.SQL - update bla set counter=?, version=? where id=? and version=?
21978 [pool-2-thread-1] DEBUG org.hibernate.SQL - update bla set counter=?, version=? where id=? and version=? 
21979 [pool-2-thread-2] DEBUG org.hibernate.engine.jdbc.batch.internal.BatchingBatch - Executing batch size: 1
21979 [pool-2-thread-1] DEBUG org.hibernate.engine.jdbc.batch.internal.BatchingBatch - Executing batch size: 1
21980 [pool-2-thread-2] DEBUG org.hibernate.jdbc.Expectations - Success of batch update unknown: 0
21980 [pool-2-thread-1] DEBUG org.hibernate.jdbc.Expectations - Success of batch update unknown: 0

然后使用批量大小为1:

12994 [pool-2-thread-2] DEBUG org.hibernate.SQL - update bla set counter=?, version=? where id=? and version=?
12994 [pool-2-thread-1] DEBUG org.hibernate.SQL - update bla set counter=?, version=? where id=? and version=?
12997 [pool-2-thread-1] DEBUG org.hibernate.cache.internal.TimestampsCacheEnabledImpl - Invalidating space [bla], timestamp: 6307671153053696
12998 [pool-2-thread-2] DEBUG org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl - JDBC transaction marked for rollback-only (exception provided for stack trace)

而现在应用程序确实抛出了预期的ObjectOp就锁失败异常

但不幸的是,这意味着在实体上使用乐观锁定的 MariaDb 和任何大于 1 的批大小是不兼容的。

龙玄天
2023-03-14

唯一可能发生的情况是MariaDB有一个错误,因为一旦Tx修改了记录,它就会锁定它,直到提交或回滚。其他Tx会因锁定而阻止更新,但在锁定释放后,必须重新评估条件。

尝试切换到READ_COMMITTED,看看它是否能解决问题。这可能是一个REPEATABLE_READ异常。

 类似资料:
  • 我正在调试使用JPA / Hibernate和Postgres(9.6.2)的Java应用程序的奇怪行为。 应用程序有 3 个实体:用户、国家/地区、用户事件。 Hibernate将其映射到4个表:users、countries、userevent、Hibernate_sequences。 用户实体具有版本列 (@Version) 和用于乐观锁定的主题。@GeneratedValue(strate

  • 我在服务层内部编写了一个更新方法,就像这样,我对所有的CRUD操作都使用Spring-data JpaRepository。 我想对此操作进行乐观锁定,因此我在 实体中添加了一个版本字段。

  • 我有一个带有和的微服务。我使用注释对中的表执行操作。我遇到的问题是保存操作花费了太多的时间,所以我希望读取操作不被它阻塞。注意,保存是通过持久化实体来执行的。 当前,在所有并发保存操作完成之前,所有读取操作都不会返回结果,这意味着表被锁定。相反,我希望实现乐观锁定。我怎样才能做到呢?

  • 我们有一个系统,我们偶尔会得到一个乐观的锁定异常。我们在代码中已经解决了这个问题,但现在我正在查看JPA 2,并看到它有一个用于处理这个问题的注释(@版本) 我们的问题是,一个表上有多个事务,如果表锁已满,则即使未对相同的记录进行更改,也会导致乐观锁定异常。 我们在JBoss 4.2服务器上使用hibernate,数据库可以是MySQL或SQL服务器。 如果改为使用@Version,这会在两个数据

  • 当我试图理解基于版本的乐观锁定如何防止“最后提交获胜”问题和适当的覆盖时,我遇到了困难。 为了使问题更具体,让我们考虑以下使用 JDBC 的伪代码: 这里,如果其他事务会在更新和提交之间提交其更改呢?如果事务是并发的,那么第一个事务所做的更新尚未提交,因此第二个事务不可见(具有适当的隔离级别),因此第一个事务提交将覆盖第二个交易的更改,而不会出现任何通知或错误。 乐观锁定只是降低了问题发生的概率,

  • 我不确定方法的设计是否恰当。 我们使用乐观锁定,将增量版本放置在每个实体上。这种实体的每次更新都是通过比较和交换算法执行的,该算法的成功与否取决于其他客户端是否同时更新实体。经典的乐观锁定,如hibernate。 我们还需要采取重新尝试的方法。我们使用基于< code>http的存储(etcd ),某些更新请求可能会超时。 这就是问题所在。如何结合乐观锁定和重试?这是我面临的具体问题。 假设我有一