1

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

场景是:

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

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

有什么建议可以避免这种竞争条件吗?

更新:

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

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

我希望能够推迟任何缓存清除操作,直到整个事务由 EJB 容器提交,但我发现无法挂钩该逻辑并使用无状态会话 bean 运行我自己的代码。

更新#2:

到目前为止,最有希望的解决方案是使用 ehcache 2.0 的 JTA 支持,除了主要的设计更改:http: //ehcache.org/documentation/apis/jta

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

4

1 回答 1

0

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

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

更新:由于这是 CMT 特定的,您应该查看SessionSynchronization接口,它是afterCompletion()方法。这在本教程中显示。

于 2012-04-28T09:01:37.460 回答