在使用CMT的Java EE服务器上,我正在使用ehcache在业务对象层(EJB)和数据访问层(使用JDBC的POJO)之间实现缓存层。我似乎在使用自填充的Ehcache的同时访问同一记录的两个线程之间遇到竞争状态。高速缓存被键入记录的主键上。

该方案是:


第一个线程更新数据库中的记录,并从缓存中删除该记录(但是数据库提交不一定立即发生-可能还有其他查询。)
第二个线程读取记录,从而导致重新填充缓存。
第一个线程提交事务。


这一切都在一秒钟之内发生。这会导致缓存与数据库不同步,并且随后的记录读取将返回过期的缓存数据,直到执行另一次更新,或者该条目从缓存中过期为止。我可以在短时间内(事务的典型长度)处理过时的数据,但不能处理几分钟(这是我想要缓存对象的时间)。

有什么避免这种竞争状况的建议吗?

更新:

在事务提交后清除缓存肯定是理想的。问题是,在使用CMT的J2EE环境中,当缓存层夹在业务层(无状态会话EJB)和数据访问层之间时,该怎么做?

为了清楚了解这带来的约束,所讨论的方法调用可能与之前或之后发生的其他方法调用在同一事务中,也可能不在同一事务中。我不能强制执行提交(或在单独的事务中完成此工作),因为这将改变客户端代码期望的事务边界。任何后续异常都不会回滚整个事务(在这种情况下,不加不必要地清除缓存是可以接受的副作用)。我无法控制事务的入口点,因为它本质上是客户端可以使用的API。将清除缓存的责任推给客户端应用程序是不合理的。

我希望能够将所有缓存清除操作推迟到EJB容器提交整个事务之前,但是我发现没有任何办法可以陷入该逻辑并使用无状态会话bean运行我自己的代码。

更新#2:

到目前为止,除了重大设计更改之外,最有前途的解决方案是使用ehcache 2.0的JTA支持:http://ehcache.org/documentation/apis/jta

这意味着升级到ehcache 2.x并为数据库启用XA事务,这可能会带来负面影响。但这似乎是“正确”的方式。

最佳答案

您正在使用事务-在commit之后(即真正发生更改的时候)删除缓存更有意义。

这样,您只能在事务处理期间看到旧数据,并且此后的所有读取都具有最新视图。

更新:由于这是CMT特定的,因此您应该查看SessionSynchronization接口,它是afterCompletion()方法。这在this tutorial.中显示

10-04 21:19
查看更多