Hibernate Search 5.5.2 - objects 上的索引更新性能不佳,关联很多
Hibernate Search 5.5.2 - Poor peformance of index update on objects with many associations
我 运行 遇到了 Hibernate Search 的严重性能问题。似乎当我 save/update/remove 通过 @IndexedEmbedded 或 @ContainedIn 引用的实体时 - parent 索引实体经历了作为索引图一部分的所有惰性 collections 的完整初始化。在某些情况下,这是从数据库中初始化和获取的关联 object 的 1000 个。我不确定这是否是预期的行为,但我想只有 updated/added 的字段需要在索引中 updated/added,并且不明白为什么我的懒惰 collections需要初始化。
这是显示我如何设置实体和搜索图的简化代码:
@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 个 parent 和许多 children。配置文件也有一组作为配置文件管理员的用户 (adminMap),以及一组收藏该配置文件的用户 (favouritesMap)。我包括了 FavouritesMap 实体 class 和关联的 ID class,AdminMap 遵循相同的结构。 Profile 实体没有直接索引,但它的扩展类型是。
这是用户执行 'favouriting' 配置文件操作时的代码:
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 字段 'profile',查找该配置文件项的索引,然后添加索引中该配置文件项的新字段 (favouritesMap.id.userId)。
然而,似乎正在发生的事情是休眠搜索正在初始化配置文件实体中的所有 collections(adminMap、favouritesMap 和 childrenProfiles)。在我的某些情况下,这会导致获取 1000 个关联实体,从而导致巨大的性能问题。 setAsFavourite 方法返回 FavouritesMap object,配置文件字段的 collections 已全部初始化,这证明了这一点。如果我删除休眠搜索注释,那么 object 会正确返回未初始化的惰性 collections,表明这是一个休眠搜索问题。
所以我的问题是,当通过 @ContainedIn 引用添加项目时,这是 hibernate 搜索初始化所有这些惰性 collections 并重新索引所有字段的正确行为吗?如果是这样,……为什么?当然,它只需要添加一个新字段而不是重新验证该实体的整个索引。如果没有,我的设置是否有任何明显的错误,或者我如何最好地调试这个问题?
谢谢
您的观察是正确的,简短的回答是:这是必需的。
任何更新都需要完全重写 Lucene 文档,即使只有一个字段发生变化。
请记住,Lucene 不是关系数据库:您不能只更新一个 "column",但它需要您重新编写文档,实质上是删除前一个并重新插入一个新副本。
无法读取现有文档,因为索引通常不是双向转换,需要将所有字段标记为 "stored" - 从性能角度来看,这也是不可取的.即使您将所有字段标记为已存储,由于操作的重新排序,读取索引文档仍然不安全,并且可能会在最终索引状态中引入不一致。
Hibernate Search 包含 "dirty checking" 的策略,这些策略超出了 Hibernate ORM 应用的策略:我们努力尝试确定是否不能跳过索引更新,但如果需要进行写入,那么确实是完整的需要读取图表以生成新文档。
除了试图限制被索引的递归字段的深度外,一种常见的技术是启用二级缓存并确保在频繁读取的关联上广泛启用它。
尤其重要的是,请确保使用以下选项清楚地界定您实际需要索引的对象图:
@IndexedEmbedded
(includePaths)
@IndexedEmbedded
(深度)
默认值可能会索引比您实际需要的更多的分支。
将来我们计划能够 "break up" 通过使用显式索引时连接将文档分为两部分,但即使我们这样做,您也需要牢记这一限制,因为Lucene 不支持关系数据库可以提供的同一种连接:我们很可能只能在一个特定点拆分文档(只能考虑一个连接)。
我 运行 遇到了 Hibernate Search 的严重性能问题。似乎当我 save/update/remove 通过 @IndexedEmbedded 或 @ContainedIn 引用的实体时 - parent 索引实体经历了作为索引图一部分的所有惰性 collections 的完整初始化。在某些情况下,这是从数据库中初始化和获取的关联 object 的 1000 个。我不确定这是否是预期的行为,但我想只有 updated/added 的字段需要在索引中 updated/added,并且不明白为什么我的懒惰 collections需要初始化。
这是显示我如何设置实体和搜索图的简化代码:
@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 个 parent 和许多 children。配置文件也有一组作为配置文件管理员的用户 (adminMap),以及一组收藏该配置文件的用户 (favouritesMap)。我包括了 FavouritesMap 实体 class 和关联的 ID class,AdminMap 遵循相同的结构。 Profile 实体没有直接索引,但它的扩展类型是。
这是用户执行 'favouriting' 配置文件操作时的代码:
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 字段 'profile',查找该配置文件项的索引,然后添加索引中该配置文件项的新字段 (favouritesMap.id.userId)。
然而,似乎正在发生的事情是休眠搜索正在初始化配置文件实体中的所有 collections(adminMap、favouritesMap 和 childrenProfiles)。在我的某些情况下,这会导致获取 1000 个关联实体,从而导致巨大的性能问题。 setAsFavourite 方法返回 FavouritesMap object,配置文件字段的 collections 已全部初始化,这证明了这一点。如果我删除休眠搜索注释,那么 object 会正确返回未初始化的惰性 collections,表明这是一个休眠搜索问题。
所以我的问题是,当通过 @ContainedIn 引用添加项目时,这是 hibernate 搜索初始化所有这些惰性 collections 并重新索引所有字段的正确行为吗?如果是这样,……为什么?当然,它只需要添加一个新字段而不是重新验证该实体的整个索引。如果没有,我的设置是否有任何明显的错误,或者我如何最好地调试这个问题?
谢谢
您的观察是正确的,简短的回答是:这是必需的。
任何更新都需要完全重写 Lucene 文档,即使只有一个字段发生变化。
请记住,Lucene 不是关系数据库:您不能只更新一个 "column",但它需要您重新编写文档,实质上是删除前一个并重新插入一个新副本。
无法读取现有文档,因为索引通常不是双向转换,需要将所有字段标记为 "stored" - 从性能角度来看,这也是不可取的.即使您将所有字段标记为已存储,由于操作的重新排序,读取索引文档仍然不安全,并且可能会在最终索引状态中引入不一致。
Hibernate Search 包含 "dirty checking" 的策略,这些策略超出了 Hibernate ORM 应用的策略:我们努力尝试确定是否不能跳过索引更新,但如果需要进行写入,那么确实是完整的需要读取图表以生成新文档。
除了试图限制被索引的递归字段的深度外,一种常见的技术是启用二级缓存并确保在频繁读取的关联上广泛启用它。
尤其重要的是,请确保使用以下选项清楚地界定您实际需要索引的对象图:
@IndexedEmbedded
(includePaths)@IndexedEmbedded
(深度)
默认值可能会索引比您实际需要的更多的分支。
将来我们计划能够 "break up" 通过使用显式索引时连接将文档分为两部分,但即使我们这样做,您也需要牢记这一限制,因为Lucene 不支持关系数据库可以提供的同一种连接:我们很可能只能在一个特定点拆分文档(只能考虑一个连接)。