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

时间:2018-10-19 12:03:07

标签: postgresql hibernate jpa mariadb optimistic-locking

我刚刚发现,当我对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=?

但是在Postgresql中将正确失败,并且使用MariaDB将会意外成功。

2 个答案:

答案 0 :(得分:1)

MariaDB唯一可能发生的错误是,一旦Tx修改了一条记录,它将锁定该记录,直到提交或回滚为止。其他Tx会由于锁定而阻止UPDATE,但是在释放锁定之后必须重新评估条件。

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

答案 1 :(得分:0)

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

似乎我在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的MariaDB:

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)

现在应用程序确实抛出了预期的ObjectOptimisticLockingFailureException

但是不幸的是,这意味着使用MariaDb对实体进行乐观锁定并且任何批处理大小大于1都是不兼容的。