休眠NONSTRICT_READ_WRITE CacheConcurrencyStrategy如何工作

介绍

在我以前的文章中 ,我介绍了READ_ONLY CacheConcurrencyStrategy ,这是不可变实体图的显而易见的选择。 当缓存的数据可变时,我们需要使用读写缓存策略,本文将介绍NONSTRICT_READ_WRITE二级缓存的工作方式。

内部运作

提交Hibernate事务后,将执行以下操作序列:

nonstrictreadwritecacheconcurrencystrategy3

首先,在刷新期间,在提交数据库事务之前,缓存无效:

  1. 当前的Hibernate事务 (例如JdbcTransactionJtaTransaction )已刷新
  2. DefaultFlushEventListener执行当前的ActionQueue
  3. EntityUpdateAction调用EntityRegionAccessStrategy更新方法
  4. NonStrictReadWriteEhcacheCollectionRegionAccessStrategy将从基础EhcacheEntityRegion中删除缓存条目

提交数据库事务后,将再次删除缓存条目:

  1. 完成回调后当前的Hibernate Transaction被调用
  2. 当前会话将此事件传播到其内部ActionQueue
  3. EntityUpdateActionEntityRegionAccessStrategy上调用afterUpdate方法
  4. NonStrictReadWriteEhcacheCollectionRegionAccessStrategy调用基础EhcacheEntityRegion上的remove方法

不一致警告

NONSTRICT_READ_WRITE模式不是“ 写式”缓存策略,因为缓存条目无效,而不是被更新。 缓存无效化与当前数据库事务不同步。 即使关联的Cache区域条目两次无效(在事务完成之前和之后),当缓存和数据库可能分开时,仍然还有一个很小的时间窗口。

以下测试将演示此问题。 首先,我们将定义Alice事务逻辑:

doInTransaction(session -> {
    LOGGER.info("Load and modify Repository");
    Repository repository = (Repository)
        session.get(Repository.class, 1L);
    assertTrue(getSessionFactory().getCache()
        .containsEntity(Repository.class, 1L));
    repository.setName("High-Performance Hibernate");
    applyInterceptor.set(true);
});

endLatch.await();

assertFalse(getSessionFactory().getCache()
    .containsEntity(Repository.class, 1L));

doInTransaction(session -> {
    applyInterceptor.set(false);
    Repository repository = (Repository)
        session.get(Repository.class, 1L);
    LOGGER.info("Cached Repository {}", repository);
});

爱丽丝加载存储库实体,并在她的第一个数据库事务中对其进行修改。
为了在Alice准备提交时产生另一个并发事务,我们将使用以下Hibernate Interceptor

private AtomicBoolean applyInterceptor = 
    new AtomicBoolean();

private final CountDownLatch endLatch = 
    new CountDownLatch(1);

private class BobTransaction extends EmptyInterceptor {
    @Override
    public void beforeTransactionCompletion(Transaction tx) {
        if(applyInterceptor.get()) {
            LOGGER.info("Fetch Repository");

            assertFalse(getSessionFactory().getCache()
                .containsEntity(Repository.class, 1L));

            executeSync(() -> {
                Session _session = getSessionFactory()
                    .openSession();
                Repository repository = (Repository) 
                    _session.get(Repository.class, 1L);
                LOGGER.info("Cached Repository {}", 
                    repository);
                _session.close();
                endLatch.countDown();
            });

            assertTrue(getSessionFactory().getCache()
                .containsEntity(Repository.class, 1L));
        }
    }
}

运行此代码将生成以下输出:

[Alice]: Load and modify Repository
[Alice]: select nonstrictr0_.id as id1_0_0_, nonstrictr0_.name as name2_0_0_ from repository nonstrictr0_ where nonstrictr0_.id=1
[Alice]: update repository set name='High-Performance Hibernate' where id=1

[Alice]: Fetch Repository from another transaction
[Bob]: select nonstrictr0_.id as id1_0_0_, nonstrictr0_.name as name2_0_0_ from repository nonstrictr0_ where nonstrictr0_.id=1
[Bob]: Cached Repository from Bob's transaction Repository{id=1, name='Hibernate-Master-Class'}

[Alice]: committed JDBC Connection

[Alice]: select nonstrictr0_.id as id1_0_0_, nonstrictr0_.name as name2_0_0_ from repository nonstrictr0_ where nonstrictr0_.id=1
[Alice]: Cached Repository Repository{id=1, name='High-Performance Hibernate'}
  1. Alice获取存储库并更新其名称
  2. 调用定制的Hibernate Interceptor并启动Bob的事务
  3. 由于存储库已从缓存中逐出,因此Bob将使用当前数据库快照加载第二级缓存
  4. Alice事务已提交,但是现在缓存包含Bob刚刚加载的先前数据库快照
  5. 如果第三位用户现在将获取存储库实体,那么他还将看到与当前数据库快照不同的陈旧实体版本。
  6. 提交Alice事务后,将再次逐出Cache条目,并且任何后续实体加载请求都将使用当前数据库快照填充Cache

过时的数据与丢失的更新

当数据库和二级缓存可能不同步时, NONSTRICT_READ_WRITE并发策略会引入一个很小的不一致窗口。 尽管这听起来可能很糟糕,但实际上,即使我们不使用二级缓存,也应始终设计应用程序来应对这些情况。 Hibernate通过其事务性的后写式第一级缓存提供应用程序级可重复读取,并且所有托管实体都将变得过时。 在将实体加载到当前的持久性上下文中之后 ,另一个并发事务可能会对其进行更新,因此,我们需要防止陈旧的数据升级为丢失的更新

乐观并发控制是处理长时间对话中丢失的更新的有效方法,并且该技术还可以缓解NONSTRICT_READ_WRITE不一致问题。

结论

NONSTRICT_READ_WRITE并发策略是大多数只读应用程序的不错选择(如果由乐观锁定机制支持)。 对于写密集型方案,缓存无效机制将增加缓存未命中率 ,因此使该技术效率低下。

翻译自: https://www.javacodegeeks.com/2015/05/how-does-hibernate-nonstrict_read_write-cacheconcurrencystrategy-work.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值