我们有一个使用 MySQL、Hibernate (3.5.1-Final) 和 EHcache(1.2.3) 作为二级缓存的 Java 应用程序。
我们的 hibernate.properties 隔离级别是 Read-committed isolation = 2
# 2-Read committed isolation
hibernate.connection.isolation=2
在大量并发事务下,我们看到某些集合(数据库关联)在加载时会引发 ObjectNotFoundException 的问题,并且似乎二级缓存正在返回该集合的旧副本。
我们有许多不同类型的事务可以访问这个集合(仅读取),并且只有几个可以从中添加/删除项目。
我们在单个事务负载甚至中等事务负载(10 - 20 个并发连接)下都看不到这个问题。
例如我们有一个 Character 实体:
@Entity
@Table(name = "CHARACTERS")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Character extends AbstractCharacter implements Serializable {
...
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@OneToMany(mappedBy = "character", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<CharacterItem> items;
通过从包含实体的集合中删除实体并调用 session.delete() 来删除实体时,我们正确地维护了对象图。
character.getItems().remove(characterItem);
session.delete(characterItem);
我们已经尝试更改 Set 项目;CacheConcurrencyStrategy 来自:
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<CharacterItem> items;
至
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<CharacterItem> items;
没有运气。
我们不使用数据库锁,而是使用乐观并发控制来捕获和重试冲突的事务。
在这一点上,我们唯一能看到的 2 个解决方案是:
尝试捕获ObjectNotFoundException并尝试智能地驱逐集合(尽管异常中似乎没有足够的上下文)
在 items 集合上使用@NotFound(action=NotFoundAction.IGNORE)注释,这将忽略并且不会抛出 ObjectNotFoundException (但我们担心它如何与二级缓存一起使用并确保它正在查看正确的数据) .
我希望有一个 @NotFound(action=NotFoundAction.EVICT_2ND_LEVEL_CACHE_RELOAD) 从缓存中驱逐该对象并尝试重新加载集合。
我们也可以尝试将 FetchyType 从 LAZY 更改为 EAGER,但我想尝试了解问题并选择最佳解决方案,以确保我们的事务中的数据在高并发下保持一致。