为什么休眠 PersistentSet.contains() 这么慢? (与 java.util.HashSet 相比)
Why hibernate PersistentSet.contains() is so slow? (compared to java.util.HashSet)
我正在开发一个用例,在该用例中,我获得了一组 ID(名为 group
),并且需要验证其中哪些 ID 位于另一个集合(名为 projectDevicesIds
)中,并且哪些没有。请注意,最后一个集合是从数据库中获取的 PersistentSet。代码非常简单如下:
Collection<String> inside = new HashSet<>();
Collection<String> notInside = new HashSet<>();
group.forEach(id -> {
if (projectDevicesIds.contains(id)) inside.add(id);
else notInside.add(id);
});
到目前为止一切顺利,问题是当 projectDevicesIds
(hibernate PersistentSet) 的大小为 100 000 且 group
包含 1000 个 id 时,此代码平均需要 200 毫秒才能 运行.当我做同样的测试但不是使用 PersistentSet 而是使用 HashSet 时,它只需要 1 毫秒!即使测试在专业上不准确,这种差异也是疯狂的并且会损害我的用例性能。在休眠中 official docs 他们说 PersistentSet 在内部使用 HashSet,所以我期望的性能大致相同。
有人可以向我解释为什么 PersistentSet.contains() 与 HashSet 相比需要这么长时间吗?并以某种方式帮助我改进此用例性能?
PersistentSet 表示数据库上的关联。这意味着当你调用 contains
时,Hibernate ORM 需要首先刷新之前可能影响关联的操作,并最终从数据库中重新加载它。或者,如果关联已延迟加载,则可能需要重新加载它。
第一次加载集合后,性能差异应该不会那么高,但这实际上取决于您如何获得 projectDevicesIds
。
如果你启用日志,你应该看看 Hibernate ORM 在你调用 contains
方法时是否需要 运行 额外的查询。
@Davide 的回答解决了 forEach
时间问题(从 200 毫秒到 1 毫秒)但是 eager
抓取结果变慢了,总时间(eager fetch
+ forEach
) 更高 ( > 250ms).
所以我想出了一个解决方法来强制使用 HashSet
(而不是 PersistentSet
)。我没有通过 Project POJO
获取设备,而是在 ProjectJpaRepositoty
中添加了自定义 SQL 查询以从特定项目获取设备 ID,但是 return class 是一个 HashSet
:
public interface ProjectRepository extends JpaRepository<Project, String> {
@Query(
value = "SELECT id FROM device WHERE project_id = :projectId",
nativeQuery = true
)
HashSet<String> getDevicesId(String projectId);
}
现在整体时间是我能得到的最低(~75ms),这是可以接受的。
我正在开发一个用例,在该用例中,我获得了一组 ID(名为 group
),并且需要验证其中哪些 ID 位于另一个集合(名为 projectDevicesIds
)中,并且哪些没有。请注意,最后一个集合是从数据库中获取的 PersistentSet。代码非常简单如下:
Collection<String> inside = new HashSet<>();
Collection<String> notInside = new HashSet<>();
group.forEach(id -> {
if (projectDevicesIds.contains(id)) inside.add(id);
else notInside.add(id);
});
到目前为止一切顺利,问题是当 projectDevicesIds
(hibernate PersistentSet) 的大小为 100 000 且 group
包含 1000 个 id 时,此代码平均需要 200 毫秒才能 运行.当我做同样的测试但不是使用 PersistentSet 而是使用 HashSet 时,它只需要 1 毫秒!即使测试在专业上不准确,这种差异也是疯狂的并且会损害我的用例性能。在休眠中 official docs 他们说 PersistentSet 在内部使用 HashSet,所以我期望的性能大致相同。
有人可以向我解释为什么 PersistentSet.contains() 与 HashSet 相比需要这么长时间吗?并以某种方式帮助我改进此用例性能?
PersistentSet 表示数据库上的关联。这意味着当你调用 contains
时,Hibernate ORM 需要首先刷新之前可能影响关联的操作,并最终从数据库中重新加载它。或者,如果关联已延迟加载,则可能需要重新加载它。
第一次加载集合后,性能差异应该不会那么高,但这实际上取决于您如何获得 projectDevicesIds
。
如果你启用日志,你应该看看 Hibernate ORM 在你调用 contains
方法时是否需要 运行 额外的查询。
@Davide 的回答解决了 forEach
时间问题(从 200 毫秒到 1 毫秒)但是 eager
抓取结果变慢了,总时间(eager fetch
+ forEach
) 更高 ( > 250ms).
所以我想出了一个解决方法来强制使用 HashSet
(而不是 PersistentSet
)。我没有通过 Project POJO
获取设备,而是在 ProjectJpaRepositoty
中添加了自定义 SQL 查询以从特定项目获取设备 ID,但是 return class 是一个 HashSet
:
public interface ProjectRepository extends JpaRepository<Project, String> {
@Query(
value = "SELECT id FROM device WHERE project_id = :projectId",
nativeQuery = true
)
HashSet<String> getDevicesId(String projectId);
}
现在整体时间是我能得到的最低(~75ms),这是可以接受的。