Java 许多并发读取+;一个写入原因ObjectNotFoundException由于ehcache

Java 许多并发读取+;一个写入原因ObjectNotFoundException由于ehcache,java,hibernate,concurrency,ehcache,Java,Hibernate,Concurrency,Ehcache,我在高流量站点上使用Hibernate 3.6.8、ehcache 2.4.5(也在最新的2.8.0中试用)、jvm 1.6.0_22,有时我会遇到 ObjectNotFoundException:不存在具有给定标识符的行:[com.example.Foo#123]` 当通过最简单的代码创建新的Foo(在本例中为id 123)时: Foo foo = new Foo(); session.save(foo); 原因是,在这个高流量网站的所有页面中,我都会得到如下的Foos: session.c

我在高流量站点上使用Hibernate 3.6.8、ehcache 2.4.5(也在最新的2.8.0中试用)、jvm 1.6.0_22,有时我会遇到

ObjectNotFoundException:不存在具有给定标识符的行:[com.example.Foo#123]`

当通过最简单的代码创建新的
Foo
(在本例中为id 123)时:

Foo foo = new Foo();
session.save(foo);
原因是,在这个高流量网站的所有页面中,我都会得到如下的
Foo
s:

session.createQuery("from Foo").setCacheable(true).list();
存储
Foo
s的表包含1000行,实体缓存在ehcache中:

<class-cache class="com.example.Foo" usage="read-write" />

我的Hibernate配置的其他可能相关部分包括:

<property name="connection.url">jdbc:mysql://localhost:3306/example?characterEncoding=UTF-8</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>

<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<property name="hibernate.c3p0.acquire_increment">1</property>
<property name="hibernate.c3p0.idle_test_period">60</property>
<property name="hibernate.c3p0.min_size">10</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.max_statements">0</property>
<property name="hibernate.c3p0.timeout">0</property>
<property name="hibernate.c3p0.acquireRetryAttempts">1</property>
<property name="hibernate.c3p0.acquireRetryDelay">1</property>

<property name="hibernate.show_sql">true</property>
<property name="hibernate.use_sql_comments">true</property>

<property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.jdbc.use_scrollable_resultset">true</property>

<property name="hibernate.cache.provider_class">net.sf.ehcache.hibernate.SingletonEhCacheProvider</property>
<property name="net.sf.ehcache.configurationResourceName">/ehcache.xml</property>
<property name="hibernate.cache.use_query_cache">true</property>
jdbc:mysql://localhost:3306/example?characterEncoding=UTF-8
com.mysql.jdbc.Driver
org.hibernate.connection.C3P0ConnectionProvider
1.
60
10
20
0
0
1.
1.
真的
真的
org.hibernate.transaction.jdbc事务工厂
线
真的
net.sf.ehcache.hibernate.SingletonEhCacheProvider
/ehcache.xml
真的
错误发生一次,然后消失。我怀疑ehcache查询缓存已使用新的实体id(123)id更新,但实体缓存尚未使用该实体的内容更新。我使用JMeter在本地相当容易地复制了这一点

有没有办法解决这个问题


Foo
创建时,会抛出一次
ObjectNotFoundException
。另一方面,如果我删除了
Foo
的一个实例,那么每次执行
.list()
,我都会不断(永远)得到
ObjectNotFoundException
。stacktrace可在上查看,该文件取自上的文档:

The following attributes and elements are optional.

timeToIdleSeconds:
Sets the time to idle for an element before it expires.
i.e. The maximum amount of time between accesses before an element expires
Is only used if the element is not eternal.
Optional attribute. A value of 0 means that an Element can idle for infinity.
The default value is 0.

timeToLiveSeconds:
Sets the time to live for an element before it expires.
i.e. The maximum time between creation time and when an element expires.
Is only used if the element is not eternal.
Optional attribute. A value of 0 means that and Element can live for infinity.
The default value is 0.
或者,您也可以选择其他选项:


读写策略不能保证数据库和缓存之间的事务性,因此我认为写操作发生时会发生这种情况:

  • 新对象Foo附加到write的hibernate会话

  • 与写请求关联的hibernate会话将延迟加载代理插入到二级缓存中

  • 新的Foo将由同一个会话插入到数据库中,但是构建、刷新和提交插入将花费一定的时间

  • 同时,另一个as请求命中代理以加载所有FOO。它在缓存中找到延迟加载代理(请参阅stacktrace
    DefaultLoadEventListener.proxyOrLoad()
    ),并决定加载对象(
    DefaultLoadEventListener.load()

  • 这将触发写入线程尚未插入数据库的Foo的Hibernate
    load()

  • 在数据库中找不到具有该Id的Foo,因此会引发
    ObjectNotFoundException


要确认这一点,请在IDE上放置一个,以查看在引发异常的那一刻,对象尚未插入DB。解决此问题的一种方法是使用该策略。

以减轻实体被删除,然后
list()
完全不起作用的情况。我在更高级别捕获了
ObjectNotFoundException
,发生这种情况时,我会:

session.getSessionFactory().getCache().evictCollectionRegions();
session.getSessionFactory().getCache().evictDefaultQueryRegion();
session.getSessionFactory().getCache().evictQueryRegions();  

清除二级缓存可使站点再次工作。当然,这并不能防止问题的发生,但它解决了整个站点的停机问题。

您可以发布您的ehcache.xml吗file@AshishJagtap:您是否已验证在访问缓存而不是数据库时是否确实引发了异常?可能是通过一个可读缓存间接执行的?
Foo
的任何外键关系?@MagicMan:是的,
Foo
表中有FK,但不一定是为了重现问题而创建或删除的条目。我的问题不是缓存过期,而是不是所有缓存区域(查询缓存和实体缓存)在知道某些内容已被放置或删除后,会快速更新。我过去曾使用过一次
事务性
ehcache,并由于发生了各种其他不一致而快速恢复。经过一些调查,结果表明原因是我的db事务隔离级别为
可重复读取
,并且应该是
READ\u COMMITTED
才能很好地工作。你的解释很有道理。解释似乎很可能,我只有一个反对意见。Foo已插入数据库(可能已提交)因为我们有一个ID。但是实体缓存尚未更新,导致另一个线程执行按ID选择的查询。该查询不会返回任何内容,因为db隔离级别是可重复读取的。因此,即使另一个线程实际提交了插入,也不会得到任何结果。