我遇到了Hibernate Search严重的性能问题。当我保存/更新/删除通过@IndexedEmbedded或@ContainedIn引用的实体时,父索引实体似乎完成了对作为索引图的一部分的所有惰性集合的完整初始化。在某些情况下,这是从数据库初始化和提取的1000个关联对象。我不确定这是否是预期的行为,但我认为只有正在更新/添加的字段需要在索引中更新/添加,并且不知道为什么我的懒惰集合需要初始化。
以下是显示我如何设置实体和搜索图表的简化代码:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Profile {
@Id
public int id;
@Field
public String name;
@IndexedEmbedded(includePaths = "name")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(referencedColumnName = "id")
public Profile parentProfile;
@ContainedIn
@OneToMany(mappedBy = "parentProfile")
public Set<Profile> childrenProfiles = new HashSet<Profile>();
@IndexedEmbedded(includePaths = { "id.userId" })
@OneToMany(mappedBy = "profile")
public Set<AdminMap> adminMap = new HashSet<AdminMap>();
@IndexedEmbedded(includePaths = { "id.userId" })
@OneToMany(mappedBy = "profile")
public Set<FavouritesMap> favouritesMap = new HashSet<FavouritesMap>();
}
@Indexed
@Entity
public class BusinessProfile extends Profile {...}
@Indexed
@Entity
public class UserProfile extends Profile {...}
@Entity
public class FavouritesMap {
@EmbeddedId
@IndexedEmbedded
public FavouritesMapId id;
@ContainedIn
@ManyToOne
@JoinColumn(insertable = false, updatable = false)
public Profile profile;
@ManyToOne
@JoinColumn(insertable = false, updatable = false)
public User user;
}
@Embeddable
public class FavouritesMapId {
@Field
public int userId;
public int profileId;
}
所以我们有一个Profile实体,可以有1个父项和多个子项。配置文件还有一组用户,这些用户是配置文件的管理员(adminMap),以及一组用户,他们最喜欢该配置文件(favouritesMap)。我已经包含了FavouritesMap实体类和关联的id类,AdminMap遵循相同的结构。 Profile实体不是直接索引的,但它的扩展类型是。
这是用户执行&#39; favouriting&#39;个人资料:
public FavouritesMap setAsFavourite(int userId, int profileId) {
FavouritesMap fav = new FavouritesMap(new FavouritesMapId(userId, profileId));
Profile profile = (Profile)entityManager.findById(Profile.class, profileId);
fav.setProfile(profile);
entityManager.save(fav);
return fav;
}
我期望发生的是当我们调用entityManager.save(fav)时,hibernate搜索会看到@ContainedIn字段&#39; profile&#39;,查找该配置文件项的索引,然后只添加新字段(favouritesMap.id.userId)到索引中的该配置文件项。
然而,似乎正在发生的事情是,hibernate搜索正在初始化配置文件实体中的所有集合(adminMap,favouritesMap和childrenProfiles)。在我的一些情况下会导致1000个相关实体被提取,从而导致巨大的性能问题。这可以通过方法setAsFavourite返回一个FavouritesMap对象来证明,该对象的配置文件字段的集合都已完全初始化。如果我删除了hibernate搜索注释,那么该对象正确地返回未初始化的惰性集合,表明它是一个休眠搜索问题。
所以我的问题是,这是hibernate搜索初始化所有这些惰性集合并通过@ContainedIn引用添加项目时重新索引所有字段的正确行为吗?如果是的话,......为什么?当然,它只需要添加一个新字段,而不是重新验证该实体的整个索引。如果没有,我的设置是否有任何明显错误,或者我如何最好地调试此问题?
由于
答案 0 :(得分:1)
您的观察是正确的,简短的回答是:这是必需的。
即使只更改了一个字段,也需要为任何更新完全重写Lucene文档。
请记住,Lucene不是关系数据库:您不能只更新一个“列”,但它需要您再次编写文档,基本上删除前一个文档并重新插入新副本。
无法读取现有文档,因为索引通常不是双向转换,需要将所有字段标记为“已存储” - 从性能角度来看,这也是不可取的。即使您要将所有字段标记为已存储,但由于操作的重新排序,读取索引文档仍然不安全,并且可能会在最终索引状态中引入不一致。
Hibernate Search包含“脏检查”策略,这些策略超出了Hibernate ORM所应用的策略:我们努力想象一下是否不能跳过索引更新,但如果写入需要发生那么确实完整的图需要阅读以产生新的文件。
除了尝试限制索引的递归字段的深度之外,一种常见的技术是启用第二级缓存,并确保在频繁读取的关联上广泛启用它。
尤其是,请确保使用以下选项清楚地划分您实际需要索引的对象图:
@IndexedEmbedded
(的的includepaths 强>)@IndexedEmbedded
(的深度强>)默认值可能是索引多于您实际需要的分支。
将来我们计划通过使用明确的索引时间连接来分解文档两部分,但即使我们这样做,你也需要记住这个限制,因为Lucene没有t支持关系数据库可以提供的相同类型的连接:我们可能只能在一个特定点拆分Document(只能考虑一个连接)。