Hibernate READ_WRITE CacheConcurrencyStrategy如何工作

介绍

在我以前的文章中,我介绍了NONSTRICT_READ_WRITE二级缓存并发机制。 在本文中,我将使用READ_WRITE策略继续本主题。

直写式缓存

NONSTRICT_READ_WRITE是一种通读缓存策略,可更新最终无效的缓存条目。 尽管这种策略可能很简单,但是随着写入操作的增加,性能会下降。 对于需要大量写入的应用程序,直写式高速缓存策略是更好的选择,因为高速缓存条目可以被日期化而不是被丢弃。

因为数据库是记录系统,并且数据库操作被包装在物理事务中,所以可以同步更新缓存(例如TRANSACTIONAL缓存并发策略的情况)或异步更新(在提交数据库事务之后)。

READ_WRITE策略是一种异步缓存并发机制,为了防止数据完整性问题(例如,陈旧的缓存条目),它使用了提供工作单元隔离保证的锁定机制。

插入资料

因为持久化的实体是唯一标识的(每个实体都分配给一个不同的数据库行),所以新创建的实体会在提交数据库事务后立即缓存:

@Override
public boolean afterInsert(Object key, Object value, Object version) throws CacheException {region().writeLock( key );try {final Lockable item = (Lockable) region().get( key );if ( item == null ) {region().put( key, new Item( value, version, region().nextTimestamp() ) );return true;}else {return false;}}finally {region().writeUnlock( key );}
}

对于要在插入时进行缓存的实体,它必须使用SEQUENCE生成器 ,该缓存由EntityInsertAction填充:

@Override
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) throws HibernateException {final EntityPersister persister = getPersister();if ( success && isCachePutEnabled( persister, getSession() ) ) {final CacheKey ck = getSession().generateCacheKey( getId(), persister.getIdentifierType(), persister.getRootEntityName() );final boolean put = cacheAfterInsert( persister, ck );}}postCommitInsert( success );
}

IDENTITY生成器不能与事务性的后写式第一级缓存设计配合使用,因此关联的EntityIdentityInsertAction不会缓存新插入的条目(至少在修复HHH-7964之前)。

从理论上讲,在数据库事务提交和第二级高速缓存插入之间,一个并发事务可能会加载新创建的实体,因此触发高速缓存插入。 虽然可能,但缓存同步滞后非常短,如果并发事务被交错,则只会使另一个事务命中数据库,而不是从缓存中加载实体。

更新资料

尽管插入实体是一个相当简单的操作,但是对于更新,我们需要同步数据库和缓存条目。 READ_WRITE并发策略采用锁定机制来确保数据完整性:

readwritecacheconcurrencystrategy_update4

  1. Hibernate Transaction提交过程触发会话刷新
  2. EntityUpdateAction用Lock对象替换当前缓存条目
  3. update方法用于同步缓存更新,因此在使用异步缓存并发策略(如READ_WRITE)时不会执行任何操作
  4. 提交数据库事务 ,将调用after-transaction-completion回调
  5. EntityUpdateAction调用EntityRegionAccessStrategy的afterUpdate方法
  6. ReadWriteEhcacheEntityRegionAccessStrategy将Lock条目替换为实际的Item ,从而封装了实体分解状态

删除资料

从下面的序列图中可以看出,删除实体与更新过程类似:

readwritecacheconcurrencystrategy_delete3

  • Hibernate Transaction提交过程触发会话刷新
  • EntityDeleteAction用Lock对象替换当前的缓存条目
  • remove方法调用不执行任何操作,因为READ_WRITE是异步缓存并发策略
  • 提交数据库事务 ,将调用after-transaction-completion回调
  • EntityDeleteAction调用EntityRegionAccessStrategy的unlockItem方法
  • ReadWriteEhcacheEntityRegionAccessStrategy用另一个超时时间增加的Lock对象替换Lock条目

删除实体后,其关联的二级缓存条目将被一个Lock对象代替,该对象将发出任何随后的请求以从数据库读取而不是使用缓存条目。

锁定构造

ItemLock类都继承自Lockable类型,并且这两个类都有一个特定的策略,允许读取或写入缓存条目。

READ_WRITE 锁定对象

Lock类定义以下方法:

@Override
public boolean isReadable(long txTimestamp) {return false;
}@Override
public boolean isWriteable(long txTimestamp, Object newVersion, Comparator versionComparator) {if ( txTimestamp > timeout ) {// if timedout then allow writereturn true;}if ( multiplicity > 0 ) {// if still locked then disallow writereturn false;}return version == null? txTimestamp > unlockTimestamp: versionComparator.compare( version, newVersion ) < 0;
}
  • Lock对象不允许读取缓存条目,因此任何后续请求都必须发送到数据库
  • 如果当前会话创建时间戳大于“锁定超时”阈值,则允许写入缓存条目
  • 如果至少一个会话设法锁定了该条目,则禁止进行任何写操作
  • 如果进入的实体状态已增加其版本,或者当前的会话创建时间戳大于当前的条目解锁时间戳,则可以使用Lock条目进行写操作

READ_WRITE 项目对象

Item类定义以下读取/写入访问策略:

@Override
public boolean isReadable(long txTimestamp) {return txTimestamp > timestamp;
}@Override
public boolean isWriteable(long txTimestamp, Object newVersion, Comparator versionComparator) {return version != null && versionComparator.compare( version, newVersion ) < 0;
}
  • 仅在缓存条目创建时间之后启动的会话中才可读取项目
  • Item条目仅在传入实体状态已增加其版本时才允许写入

缓存条目并发控制

当保存和读取底层缓存条目时,将调用这些并发控制机制。

在调用ReadWriteEhcacheEntityRegionAccessStrategy get方法时读取缓存条目:

public final Object get(Object key, long txTimestamp) throws CacheException {readLockIfNeeded( key );try {final Lockable item = (Lockable) region().get( key );final boolean readable = item != null && item.isReadable( txTimestamp );if ( readable ) {return item.getValue();}else {return null;}}finally {readUnlockIfNeeded( key );}
}

缓存条目由ReadWriteEhcacheEntityRegionAccessStrategy putFromLoad方法编写:

public final boolean putFromLoad(Object key,Object value,long txTimestamp,Object version,boolean minimalPutOverride)throws CacheException {region().writeLock( key );try {final Lockable item = (Lockable) region().get( key );final boolean writeable = item == null || item.isWriteable( txTimestamp, version, versionComparator );if ( writeable ) {region().put( key, new Item( value, version, region().nextTimestamp() ) );return true;}else {return false;}}finally {region().writeUnlock( key );}
}

超时

如果数据库操作失败,则当前高速缓存条目将保留一个Lock对象,并且无法回滚到其先前的Item状态。 由于这个原因,锁必须超时,以允许将缓存条目替换为实际的Item对象。 EhcacheDataRegion定义以下超时属性:

private static final String CACHE_LOCK_TIMEOUT_PROPERTY = "net.sf.ehcache.hibernate.cache_lock_timeout";
private static final int DEFAULT_CACHE_LOCK_TIMEOUT = 60000;

除非我们重写net.sf.ehcache.hibernate.cache_lock_timeout属性,否则默认超时为60秒:

final String timeout = properties.getProperty(CACHE_LOCK_TIMEOUT_PROPERTY,Integer.toString( DEFAULT_CACHE_LOCK_TIMEOUT )
);

以下测试将模拟失败的数据库事务,因此我们可以观察到READ_WRITE缓存如何仅在超时阈值到期后才允许写入。 首先,我们将降低超时值,以减少缓存冻结时间:

properties.put("net.sf.ehcache.hibernate.cache_lock_timeout", String.valueOf(250));

我们将使用自定义拦截器手动回滚当前正在运行的事务:

@Override
protected Interceptor interceptor() {return new EmptyInterceptor() {@Overridepublic void beforeTransactionCompletion(Transaction tx) {if(applyInterceptor.get()) {tx.rollback();}}};
}

以下例程将测试锁定超时行为:

try {doInTransaction(session -> {Repository repository = (Repository)session.get(Repository.class, 1L);repository.setName("High-Performance Hibernate");applyInterceptor.set(true);});
} catch (Exception e) {LOGGER.info("Expected", e);
}
applyInterceptor.set(false);AtomicReference<Object> previousCacheEntryReference =new AtomicReference<>();
AtomicBoolean cacheEntryChanged = new AtomicBoolean();while (!cacheEntryChanged.get()) {doInTransaction(session -> {boolean entryChange;session.get(Repository.class, 1L);try {Object previousCacheEntry = previousCacheEntryReference.get();Object cacheEntry = getCacheEntry(Repository.class, 1L);entryChange = previousCacheEntry != null &&previousCacheEntry != cacheEntry;previousCacheEntryReference.set(cacheEntry);LOGGER.info("Cache entry {}", ToStringBuilder.reflectionToString(cacheEntry));if(!entryChange) {sleep(100);} else {cacheEntryChanged.set(true);}} catch (IllegalAccessException e) {LOGGER.error("Error accessing Cache", e);}});
}

运行此测试将生成以下输出:

selectreadwritec0_.id as id1_0_0_,readwritec0_.name as name2_0_0_,readwritec0_.version as version3_0_0_ 
fromrepository readwritec0_ 
wherereadwritec0_.id=1updaterepository 
setname='High-Performance Hibernate',version=1 
whereid=1 and version=0JdbcTransaction - rolled JDBC Connectionselectreadwritec0_.id as id1_0_0_,readwritec0_.name as name2_0_0_,readwritec0_.version as version3_0_0_ 
fromrepository readwritec0_ 
wherereadwritec0_.id = 1Cache entry net.sf.ehcache.Element@3f9a0805[key=ReadWriteCacheConcurrencyStrategyWithLockTimeoutTest$Repository#1,value=Lock Source-UUID:ac775350-3930-4042-84b8-362b64c47e4b Lock-ID:0,version=1,hitCount=3,timeToLive=120,timeToIdle=120,lastUpdateTime=1432280657865,cacheDefaultLifespan=true,id=0
]
Wait 100 ms!
JdbcTransaction - committed JDBC Connectionselectreadwritec0_.id as id1_0_0_,readwritec0_.name as name2_0_0_,readwritec0_.version as version3_0_0_ 
fromrepository readwritec0_ 
wherereadwritec0_.id = 1Cache entry net.sf.ehcache.Element@3f9a0805[key=ReadWriteCacheConcurrencyStrategyWithLockTimeoutTest$Repository#1,value=Lock Source-UUID:ac775350-3930-4042-84b8-362b64c47e4b Lock-ID:0,version=1,hitCount=3,timeToLive=120,timeToIdle=120,lastUpdateTime=1432280657865,cacheDefaultLifespan=true,id=0
]
Wait 100 ms!
JdbcTransaction - committed JDBC Connectionselectreadwritec0_.id as id1_0_0_,readwritec0_.name as name2_0_0_,readwritec0_.version as version3_0_0_ 
fromrepository readwritec0_ 
wherereadwritec0_.id = 1
Cache entry net.sf.ehcache.Element@305f031[key=ReadWriteCacheConcurrencyStrategyWithLockTimeoutTest$Repository#1,value=org.hibernate.cache.ehcache.internal.strategy.AbstractReadWriteEhcacheAccessStrategy$Item@592e843a,version=1,hitCount=1,timeToLive=120,timeToIdle=120,lastUpdateTime=1432280658322,cacheDefaultLifespan=true,id=0
]
JdbcTransaction - committed JDBC Connection
  • 第一个事务尝试更新实体,因此在提交事务之前,关联的第二级缓存条目已被锁定。
  • 第一个事务失败,它被回滚
  • 持有锁,因此接下来的两个连续事务将进入数据库,而不用当前已加载的数据库实体状态替换Lock条目
  • 在Lock超时期限到期后,第三笔交易最终可以用Item缓存条目替换Lock (保持实体分解为水合状态

结论

READ_WRITE并发策略具有直写式缓存机制的优点,但是您需要了解它的内部工作原理,才能确定它是否适合您当前的项目数据访问要求。

对于繁重的写争用方案,锁定结构将使其他并发事务进入数据库,因此您必须确定同步高速缓存并发策略是否更适合这种情况。

  • 代码可在GitHub上获得 。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/360134.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java hashmap替换key,HashMap 用可变对象作为 key 踩坑

点击上方☝Java编程技术乐园&#xff0c;轻松关注&#xff01;及时获取有趣有料的技术文章做一个积极的人编码、改bug、提升自己我有一个乐园&#xff0c;面向编程&#xff0c;春暖花开&#xff01;作者&#xff1a;Icharlehttps://icharle.com/hashmapkebianobj.html前言在 Ja…

(转)Eclipse平台技术概述

转载&#xff1a;周金根 http://zhoujg.blog.51cto.com/1281471/516833Eclipse&#xff1a;Eclipse平台技术概述2010-10-19 13:35:00标签&#xff1a;Eclipse 休闲 职场原创作品&#xff0c;允许转载&#xff0c;转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明…

带有光纤的可扩展,健壮和标准的Java Web服务

这篇博客文章讨论了负载下的基准Web服务性能。 要了解有关Web服务性能理论的更多信息&#xff0c;请阅读利特尔定律&#xff0c;可伸缩性和容错 。 使用阻塞和异步IO对Web服务进行基准测试 Web应用程序&#xff08;或Web服务&#xff09;如何在负载下&#xff0c;面对各种故障…

php js 图片旋转,jQuery实现可以控制图片旋转角度效果

本文实例讲述了jQuery实现可以控制图片旋转角度效果。分享给大家供大家参考&#xff0c;具体如下&#xff1a;运行效果截图如下&#xff1a;具体代码如下&#xff1a;/p>"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">img { margin-top:100px; m…

【OAuth】快速入门

一、引言 1、什么是OAuth2.0&#xff1f; OAuth 2.0是一个关于授权的开放网络协议&#xff0c;允许用户授权第三方应用访问其在服务提供商上存储的资源&#xff08;如照片、视频、联系人列表&#xff09;&#xff0c;而无需将用户名和密码提供给第三方应用。OAuth 2.0在第三方应…

jert oracle 统计说明,Oracle JET简单入门(一)Oracle JET介绍

Oracle JET (Oracle Javascript Extension Toolkit)是一款 Oracle 的 JavaScript 拓展工具包。简单来说 Oracle JET 是一个一堆好用的前端工具结合体。Oracle JET 文档链接 http://docs.oracle.com/middleware/jet310/jet/developer/toc.htmOracle JET支持 Model-View-ViewMod…

SELECT语句使用JDBC和Hibernate批量获取

介绍 现在&#xff0c;我已经介绍了Hibernate对INSERT &#xff0c; UPDATE和DELETE语句的批处理支持&#xff0c;是时候分析SELECT语句结果集的批量提取了。 JDBC ResultSet提供了一个客户端代理游标&#xff0c;用于获取当前语句的返回数据。 执行该语句后&#xff0c;必须将…

Spring环境的搭建与测试 (spring2.5.6)

这里是采用的视频里面的spring版本 下载spring2.5.6&#xff0c; 然后进行解压缩&#xff0c;在解压目录中找到下面jar文件&#xff0c;拷贝到类路径下 dist\spring.jar lib\jakarta-commons\commons-logging.jar 上边两个是基本的jar包。。 如果使用了切面编程(AOP),还需要下列…

如何使用Hibernate批处理INSERT和UPDATE语句

介绍 JDBC长期以来一直为DML语句批处理提供支持。 默认情况下&#xff0c;所有语句都一个接一个地发送&#xff0c;每个语句都在单独的网络往返中发送。 批处理使我们能够一次性发送多个语句&#xff0c;从而节省了不必要的套接字流刷新。 Hibernate将数据库语句隐藏在事务后写…

linux 查看系统组账号密码是什么,Linux 用户与组管理详解(system-config-users 命令行)...

用户与组管理用户相关文件组账号相关文件用户和组管理软件&#xff1a;基于命令行的用户和组管理创建用户查看用户信息删除用户修改用户信息为用户创建密码更改用户密码信息创建组删除组查看当前登录到系统的用户用户与组管理什么是用户&#xff0c;用户是人吗&#xff1f;用户…

linux没有root密码xshell,LINUX终端免密登陆(以Xshell为例)

1&#xff0c;工具-新建用户密钥生成向导linux2&#xff0c;密钥类型选择&#xff1a;RSA&#xff0c;而后下一步shell3&#xff0c;输入密钥名称和密码ssh4&#xff0c;公钥格式为SSH-OpenSSH,保存为文件(后缀为pub)(记录此文件目录)工具二&#xff0c;登陆在须要免密登陆的主…

ActionBar之style出现Cannot resolve symbol 'Theme' 错误

今天 2014/03/08 00:49 刚刚升级 android studio 到了 0.5.0 版本&#xff0c;修复了许多 bug&#xff0c;包含当前这个问题&#xff0c;之前一直困扰我很久&#xff0c;莫名奇妙的提示主题样式找不到&#xff0c;无法解析&#xff0c; 后来一直谷歌发现很多人都认为是 IDE 的b…

单片机上运行linux程序代码,在Linux下烧录51单片机

原标题&#xff1a;在Linux下烧录51单片机*本文作者&#xff1a;LEdge1&#xff0c;本文属 FreeBuf原创奖励计划&#xff0c;未经许可禁止转载。背景我一直在学习Linux 系统&#xff0c;但是最近还要学习51单片机&#xff0c;所以在Linux下给51单片机烧录程序那是非常必要的。之…

linux运行core控制台程序,VisualStudioCode创建的asp.net core控制台程序部署到linux

1、asp.net core控制台程序static void Main(string[] args){int times10;while(times>0){Console.WriteLine("Hello World!");times--;Thread.Sleep(1000);}}2、发布发布前&#xff0c;修改test2.csproj文件(项目名称为test2)Exenetcoreapp2.1centos.7-x64主要添…

wps linux版本支持vba,Wps vba安装包

wps vba是款专用于wps办公软件的宏插件&#xff0c;可以利用VBA制作Excel登录系统&#xff0c;实现一些VB无法实现的功能&#xff0c;操作界面人性化&#xff0c;方便用户的操作&#xff0c;还可以利用VBA来Excel内轻松开发出功能强大的自动化程序。软件简介&#xff1a;wps vb…

我的Dojo中有一个Mojo(如何编写Maven插件)

我一直忙于在工作中使用Maven的腋窝。 对于很多开发人员&#xff0c;我会听到&#xff1a;“那又怎样。” 不同之处在于&#xff0c;我通常在无法直接访问Internet的环境中工作。 因此&#xff0c;当我说我经常使用Maven时&#xff0c;这意味着某些事情。 依赖地狱 公平地说&a…

在linux下安装mongo数据库,Linux系统下安装MongoDB

MongoDB提供了Linux系统上32位和64位的安装包&#xff0c;你可以在官网下载安装包。下载完安装包&#xff0c;并解压 tgz(以下演示的是 64 位 Linux上的安装) 。curl-O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.0.6.tgz # 下载tar-zxvf mongodb-linux-x86_64-…

Windows 8.1 新增控件之 Hyperlink

Windows 8.1 新增控件之 Hyperlink 原文:Windows 8.1 新增控件之 HyperlinkHyperlink 控件应该不用过多介绍大家肯定十分清楚其作用&#xff0c;它的功能就像HTML中的<a href””>标签一样&#xff0c;只不过是在XAML中实现。 使用Hyperlink 标记的文字在应用中会以特殊颜…

Linux的slab和nginx的区别,Nginx核心知识100讲》nginx Slab管理器

极客专栏《Nginx核心知识100讲》38小节的笔记nginx 不同的worker之间需要共享信息的时候&#xff0c;只能通过共享内存。共享内存会使用链表&#xff0c;红黑树这样的数据结构。但是每个红黑树上有很多节点&#xff0c;每个节点上都需要分配内存去存放。怎样把一整块共享内存切…

睡觉时:新增的Java 8新增功能

自Java 8推出以来&#xff0c;最有趣的功能是什么&#xff1f; Java 8最近庆祝了它的第一个生日&#xff0c;而主要版本刚刚一年多前发布。 这当然值得庆祝。 自从最初的Java 8版本问世以来&#xff0c;已经发布了六个更新。 这些更新中的一些元素是次要的&#xff0c;但是如…