我正在使用JBoss EAP 4.3。
当使用内置的JBoss TreeCache作为Hibernate的二级缓存时,我正在研究并发策略的不同选项。我已经设置好了,我已经通过查看日志来验证缓存是否正常工作,但我不确定实际使用的是什么并发策略以及它的工作方式。
对于每个实体,我可以在@Cache
注释中设置以下“使用”值之一:NONE
,READ_ONLY
,NONSTRICT_READ_WRITE
,READ_WRITE
,TRANSACTIONAL
。
另一方面,在我的JBossTreeCache
配置文件中,我可以将IsolationLevel
设置为整个缓存的以下之一:NONE
,READ_UNCOMMITTED
,{{1 },READ_COMMITTED
,REPEATABLE_READ
(或只使用SERIALIZABLE
)。
一次查看配置选项时,文档非常清楚,但我想知道当您组合不同选项时会发生什么。
例如,如果您为实体设置OPTIMISTIC
,但为@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
设置NONE
为IsolationLevel
,会发生什么?
我还认为JBossTreecache
仅支持JBossTreeCache
,NONE
和READ_ONLY
使用,但是您允许将TRANSACTIONAL
与它们结合使用?如果你使用例如IsolationLevel
?
总之,这里应该有5x6种不同的组合,但并非所有组合都有意义。
任何人都可以帮我解决这个问题吗?
答案 0 :(得分:8)
隔离级别是一个棘手的问题。
例如,如果您为实体设置
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
,但为NONE
设置IsolationLevel
为JBossTreecache
,会发生什么?
大多数情况下,生产中难以发现的错误...你应该明白,通过使用读写缓存,你实际上已经陷入了分布式交易中的所有“细节”。
好的,关于组合: 当对象不更改时,应使用Hibernate中的只读缓存设置。例如,对于国家字典。缓存并发级别NONE或READ_ONLY应该与它一起使用。
当您的缓存对象发生更改时,应使用非严格读写,但这种情况很少发生,竞争条件的可能性很小。例如,对于时区词典 - 时区可能偶尔出现/消失,但这种情况可能每年发生几次。同样,缓存并发级别NONE或READ_ONLY应该与它一起使用。
现在,更有趣的组合。
Hibernate中的 Transactional
缓存不安全,Hibernate假定缓存更新是事务性的,但没有做任何事情来确保它。所以你必须使用一个完整的外部XA(分布式事务)协调器,除非你真的知道你在做什么,否则你真的真的不想要它。最有可能的是,您必须使用完整的EJB3容器来支持XA-manager,尽管可以使用像http://www.atomikos.com/这样的外部事务管理器和普通的servlet + Spring。显然,你需要使用TRANSACTIONAL
缓存。
'READ_WRITE`是一个有趣的组合。在这种模式下,Hibernate本身就是一个轻量级的XA协调器,所以它不需要一个完整的外部XA。简要说明它的工作原理:
flush()
期间(可能在事务生存期内多次出现,但通常在提交之前发生)Hibernate会通过一个会话并搜索更新/插入/删除的对象。然后,这些对象首先保存到数据库中,然后在缓存中锁定并更新,以便并发事务既不能更新也不能读取。这里有几点好处:
可能的可重复读取违规。想象一下,我们有事务A(tA)和事务B(tB)同时启动,并且加载对象X,tA然后修改此对象,然后提交tA。在许多使用快照隔离的数据库(Oracle,PostgreSQL,FireBird)中,如果tB再次请求对象X,它应该接收与事务开始时相同的对象状态。但是,READ_WRITE缓存可能违反了这种情况 - 那里没有快照隔离。 Hibernate试图通过在缓存对象上使用时间戳来解决它,但是在计时器分辨率较差的操作系统上(Windows上为15.6ms),可以保证让一些比赛漏掉。
可能的乐观陈旧对象版本 - 如果您非常不幸在Windows上工作,并且有多个事务提交具有相同的时间戳,则可能会获得过时的对象版本。