一、什么是基准测试
参考文档:
基准测试是一种性能测试。
通过设计合理的测试方法,选用合适的测试工具和被测系统,实现对某个特定目标场景的某项性能指标进行定量的和可对比的测试。
特质:
-
可重复
可进行重复性的测试,这样做有利于比较每次的测试结果,得到性能结果的长期变化趋势,为系统调优和上线前的容量规划做参考。
-
可观测
通过全方位的监控(包括测试开始到结束,执行机、服务器、数据库),及时了解和分析测试过程发生了什么。
-
可展示
相关人员可以直观明了的了解测试结果(web界面、仪表盘、折线图树状图等形式)。
-
真实
测试的结果反映了客户体验到的真实的情况(真实准确的业务场景+与生产一致的配置+合理正确的测试方法)。
-
可执行
相关人员可以快速的进行测试验证修改调优(可定位可分析)。
二、JMH (Java Microbenchmark Harness)
2.1 POM
jdk9自带,低于jdk9需引入。
<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-generator-annprocess -->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.23</version>
<scope>provided</scope>
</dependency>
2.2 demo
参考文档:
[Spring, JMH] Howto integrate JMH benchmarks with Spring · GitHub
https://yangbingdong.com/2018/spring-boot-learning-testing/
2.2.1 代码
非最佳实现,仅供参考。
@State(Scope.Benchmark)
@BenchmarkMode(Mode.Throughput)
public class SimpleBenchmark {
private ConfigurableApplicationContext context;
private SimpleBean simpleBean;
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(SimpleBenchmark.class.getSimpleName())
.build();
new Runner(opt).run();
}
@Setup
public void initDemo() {
if (context == null) {
context = SpringApplication.run(JmhApplication.class);
}
if (simpleBean == null) {
simpleBean = context.getBean(SimpleBean.class);
}
}
@TearDown
public void closeContext() {
if (context != null) {
context.close();
}
}
@Benchmark
@Fork(0)
@Threads(10)
@Warmup(iterations = 5)
@Measurement(iterations = 10)
public String testIndex() throws ParamException {
// mock
return simpleBean.hello();
}
}
A.基本概念
@State(Scope.Benchmark)
当存在类属性时需指定@State,如demo中指benchmark共享context,否则每次都会创建ApplicationContext。
@Benchmark
相当于Junit中的@Test。
@Warmup(iterations = 5)
预热5轮。
@Measurement(iterations = 10)
度量10轮。
@Fork
含义是为每个benchmark创建几个独立进程,默认是1。
此处如果不设置为0则无法正常启动,应该跟springboot有关【TODO】。
@Threads
每个fork进程使用多少条线程去执行你的测试方法,默认值是Runtime.getRuntime().availableProcessors()。
@Setup 和 @TearDown
在执行 benchmark 之前/之后被执行。
@BenchmarkMode(Mode.Throughput)
Mode有多种选项,可同时支持。Throughput代表整体吞吐量,例如“1秒内可以执行多少次调用”。
B.bean
使用@Autowired或@Resource均无法注入。
2.2.2 实战
基于以上demo,对富文本搜索的如下两种实现方式进行了简单对比。
-
jpa,
SQL LIKE '%text%'
-
hibernate-search
A. jpa
Benchmark Mode Cnt Score Error Units
SearchQueryBenchmark.testSql thrpt 10 1768.603 ± 7.480 ops/s
B.hibernate-search
Benchmark Mode Cnt Score Error Units
SearchIndexBenchmark.testIndex thrpt 10 1564.827 ± 49.493 ops/s
C.小结
hibernate-search
略差于SQL LIKE
。
以上对比仅供参考,因为测试切入点过小:
1.数据量小,富文本文档数包含已删除在内仅四百余篇,且每篇文档的内容并不长。
2.搜索文本固定,长度固定,内容固定。
三、实战
补充文档数至2414.
3.1hibernate-search
Benchmark Mode Cnt Score Error Units
SearchIndexBenchmark.testIndex thrpt 10 1251.540 ± 8.028 ops/s
3.2jpa
Benchmark Mode Cnt Score Error Units
SearchQueryBenchmark.testSql thrpt 10 1429.534 ± 8.013 ops/s
jpa还是比hibernate-search高200,这样感觉都不用上索引了。
去掉hibernate-search的分词设置。
1.jpa
Benchmark Mode Cnt Score Error Units
SearchQueryBenchmark.testSql thrpt 10 1539.728 ± 35.181 ops/s
2.hibernate-search
Benchmark Mode Cnt Score Error Units
SearchIndexBenchmark.testIndex thrpt 10 1638.074 ± 111.799 ops/s
数据量还原至440
1.jpa
Benchmark Mode Cnt Score Error Units
SearchQueryBenchmark.testSql thrpt 10 2656.599 ± 47.034 ops/s
2.hibernate-search
Benchmark Mode Cnt Score Error Units
SearchIndexBenchmark.testIndex thrpt 10 1986.480 ± 29.448 ops/s
创建索引时过滤掉已删除数据,结果差别不大
Benchmark Mode Cnt Score Error Units
SearchIndexBenchmark.testIndex thrpt 10 1901.504 ± 39.296 ops/s