Hibernate Validation 6.0 ListValueExtractor.extractValues 似乎在处理大型列表时性能不佳
Hibernate Validation 6.0 ListValueExtractor.extractValues seems to have poor performance with large lists
我正在使用 Hibernate 验证 6.x。我正在验证的对象中有一个字段,其中包含一个列表,例如 List<@NotNull Double> doubles
。我面临的问题是,当列表非常大时,性能会大幅下降。为了调查这个问题,我将列表元素的验证实现为列表上的自定义验证器,@ValidDoubles List<Double> doubles
,使用流迭代元素,并为该验证器实现了约 65% 的性能改进。
分析应用程序后,我可以看到大部分时间花在 ListValueExtractor.extractValues
上,可以在 here 中找到。我希望有人能解释为什么这种方法看起来如此昂贵,以及是否有任何已知的解决方法。
示例对象:
public class myDataObject {
private List<@NotNull Double> doubles // List which can contain thousands of values
// Getters and Setters
}
更新
经过进一步的分析和调查,我认为这个问题与 Hibernate 在执行级联验证时跟踪哪些 bean 已经过验证有关,特别是在这样做时使用 System.identityHashCode
(Here is the code).
查看我的探查器,我可以看到 11.6% 的 CPU 时间花在了验证输入 bean 上。其中,11.3% 的时间用于调用 System.identityHashCode
。有趣的是,它是花费时间的第二个子对象,即使它们包含相对简单的验证。我想知道我是否错误地配置了验证器或 beans,因为这看起来很奇怪。
我的验证器配置如下所示:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationPropertyMap">
<util:map>
<entry key="hibernate.validator.fail_fast" value="true"/>
</util:map>
</property>
</bean>
验证器调用:
Set<ConstraintViolation<InputObject>> violations = validator.validate(input);
示例对象结构
public class InputObject {
@NotNull
String name;
@Valid
List<FirstChild> firstChildren; // on average 10 objects but can be up to very large
// Getters and Setters
}
public class FirstChild {
@SomeCustomValidator // Not important
Integer someValue;
// 3 to 4 further fields with simple validators
@Valid
List<SecondChild> secondChildren; // On average around 40 objects but can be very large
// Getters and Setters
}
public class SecondChild {
@NotBlank
String foo;
@NotBlank
String bar;
// Getters and Setters
}
结论:
- 从探查器来看,问题在于从列表上的
@Valid
注释中发现的级联验证。
- 问题似乎与 Hibernate 在执行级联验证时跟踪哪些对象已经过验证有关。
- 探查器显示
System.identityHashCode
是占用验证时间最多的方法。
这是 Hibernate 的优化问题,还是我可以以某种方式配置我的验证器或输入对象结构以产生更好的性能?
艰难的。
所以你看到的问题是我们为每个列表值创建了一个 BeanGroupProcessedUnit
,所以当你有很多时,它不能很好地扩展。
将内容移出列表时不会出现问题,因为我们只为整个列表保留一个已处理的单元。
我不完全确定是否有一个不会破坏其他用例的简单修复方法,但我们至少应该检查一下我们是否可以改善您遇到的情况。
话虽如此,如果您能抽出时间在我们的跟踪器上打开一个问题,我将不胜感激 https://hibernate.atlassian.net/projects/HV/issues with a reproducer based on https://github.com/hibernate/hibernate-test-case-templates/tree/master/validator?这将有助于启动进程。
我正在使用 Hibernate 验证 6.x。我正在验证的对象中有一个字段,其中包含一个列表,例如 List<@NotNull Double> doubles
。我面临的问题是,当列表非常大时,性能会大幅下降。为了调查这个问题,我将列表元素的验证实现为列表上的自定义验证器,@ValidDoubles List<Double> doubles
,使用流迭代元素,并为该验证器实现了约 65% 的性能改进。
分析应用程序后,我可以看到大部分时间花在 ListValueExtractor.extractValues
上,可以在 here 中找到。我希望有人能解释为什么这种方法看起来如此昂贵,以及是否有任何已知的解决方法。
示例对象:
public class myDataObject {
private List<@NotNull Double> doubles // List which can contain thousands of values
// Getters and Setters
}
更新
经过进一步的分析和调查,我认为这个问题与 Hibernate 在执行级联验证时跟踪哪些 bean 已经过验证有关,特别是在这样做时使用 System.identityHashCode
(Here is the code).
查看我的探查器,我可以看到 11.6% 的 CPU 时间花在了验证输入 bean 上。其中,11.3% 的时间用于调用 System.identityHashCode
。有趣的是,它是花费时间的第二个子对象,即使它们包含相对简单的验证。我想知道我是否错误地配置了验证器或 beans,因为这看起来很奇怪。
我的验证器配置如下所示:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationPropertyMap">
<util:map>
<entry key="hibernate.validator.fail_fast" value="true"/>
</util:map>
</property>
</bean>
验证器调用:
Set<ConstraintViolation<InputObject>> violations = validator.validate(input);
示例对象结构
public class InputObject {
@NotNull
String name;
@Valid
List<FirstChild> firstChildren; // on average 10 objects but can be up to very large
// Getters and Setters
}
public class FirstChild {
@SomeCustomValidator // Not important
Integer someValue;
// 3 to 4 further fields with simple validators
@Valid
List<SecondChild> secondChildren; // On average around 40 objects but can be very large
// Getters and Setters
}
public class SecondChild {
@NotBlank
String foo;
@NotBlank
String bar;
// Getters and Setters
}
结论:
- 从探查器来看,问题在于从列表上的
@Valid
注释中发现的级联验证。 - 问题似乎与 Hibernate 在执行级联验证时跟踪哪些对象已经过验证有关。
- 探查器显示
System.identityHashCode
是占用验证时间最多的方法。
这是 Hibernate 的优化问题,还是我可以以某种方式配置我的验证器或输入对象结构以产生更好的性能?
艰难的。
所以你看到的问题是我们为每个列表值创建了一个 BeanGroupProcessedUnit
,所以当你有很多时,它不能很好地扩展。
将内容移出列表时不会出现问题,因为我们只为整个列表保留一个已处理的单元。
我不完全确定是否有一个不会破坏其他用例的简单修复方法,但我们至少应该检查一下我们是否可以改善您遇到的情况。
话虽如此,如果您能抽出时间在我们的跟踪器上打开一个问题,我将不胜感激 https://hibernate.atlassian.net/projects/HV/issues with a reproducer based on https://github.com/hibernate/hibernate-test-case-templates/tree/master/validator?这将有助于启动进程。