服务器之家:专注于服务器技术及软件下载分享
分类导航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|数据库技术|

服务器之家 - 数据库 - MongoDB - Jmh基准测试,看我怎么用它来测试Mongodb的数据加载性能

Jmh基准测试,看我怎么用它来测试Mongodb的数据加载性能

2023-05-07 04:08未知服务器之家 MongoDB

[[410165]] 本文转载自微信公众号「稀饭下雪」,作者帅气的小饭饭 。转载本文请联系稀饭下雪公众号。 「主管小肥肥:」 最近我们这边引入了mongodb,不过没有实际上测试过性能如何,只是听说读写比mysql快,你今天没有什么排期

 [[410165]]

本文转载自微信公众号「稀饭下雪」,作者帅气的小饭饭 。转载本文请联系稀饭下雪公众号。

「主管小肥肥:」 最近我们这边引入了mongodb,不过没有实际上测试过性能如何,只是听说读写比mysql快,你今天没有什么排期,测试一下,然后今天内给我个答案吧

「小饭饭:」 好的,接下来就是测试性能的一天了。

到了这里,可能大部分人的第一想法应该是直接用这种方式:

  1. public void test() { 
  2.     long start = System.currentTimeMillis(); 
  3.     // 执行逻辑 
  4.     long end = System.currentTimeMillis();   
  5.     System.out.println(end - start); 

no,我这次使用的是JMH

无论出自何种原因需要进行性能评估,量化指标总是必要的,那么如何量化呢?

这就需要我们的主角 JMH 登场了!

先给你们看个效果图

Jmh基准测试,看我怎么用它来测试Mongodb的数据加载性能

性能对比图

什么是JMH

JMH(Java Microbenchmark Harness)是用于代码微基准测试的工具套件,主要是基于方法层面的基准测试,精度可以达到纳秒级。

该工具是由 Oracle 内部实现 JIT 的大牛们编写的,他们应该比任何人都了解 JIT 以及 JVM 对于基准测试的影响。

当你定位到热点方法,希望进一步优化方法性能的时候,就可以使用 JMH 对优化的结果进行量化的分析。

JMH 比较典型的应用场景如下:

  • 想准确地知道某个方法需要执行多长时间,以及执行时间和输入之间的相关性
  • 对比接口不同实现在给定条件下的吞吐量
  • 查看多少百分比的请求在多长时间内完成

下面我们以mongodb、hibernate、jdbc数据加载性能对比为例,使用 JMH 做基准测试。

怎么做JMH基准测试?

  • 加入依赖

因为 JMH 是 JDK9 自带的,如果是 JDK9 之前的版本需要加入如下依赖:

  1. <dependency> 
  2.     <groupId>org.openjdk.jmh</groupId> 
  3.     <artifactId>jmh-core</artifactId> 
  4.     <version>1.29</version> 
  5. </dependency> 
  6. <dependency> 
  7.     <groupId>org.openjdk.jmh</groupId> 
  8.     <artifactId>jmh-generator-annprocess</artifactId> 
  9.     <version>1.29</version> 
  10. </dependency> 
  • 编写基准测试

接下来,创建一个 JMH 测试类,具体代码如下所示:

  1. @BenchmarkMode({Mode.AverageTime}) 
  2. @Warmup(iterations = 1, time = 5) 
  3. @Measurement(iterations = 3, time = 5) 
  4. @Threads(1) 
  5. @Fork(1) 
  6. @OutputTimeUnit(TimeUnit.MILLISECONDS) 
  7. @State(Scope.Benchmark) 
  8. public class ReadBenchMarks { 
  9.     @Benchmark 
  10.     public void loadMongoTemplate(){ 
  11.         // mongoTemplate数据加载 
  12.     } 
  13.  
  14.     @Benchmark 
  15.     public void loadMongoDriver(){ 
  16.         // mongoDriver数据加载 
  17.     } 
  18.  
  19.     @Benchmark 
  20.     public void loadHibernate(){ 
  21.         // hibernate数据加载 
  22.     } 
  23.  
  24.     @Benchmark 
  25.     public void loadJdbc(){ 
  26.         // jdbc数据加载 
  27.     } 
  28.  
  29.     public static void main(String[] args) throws RunnerException { 
  30.         Options options = new OptionsBuilder() 
  31.                 .include(ReadBenchMarks.class.getSimpleName()) 
  32.                 .output("db.log"
  33.                 .build(); 
  34.         new Runner(options).run(); 
  35.     } 

「核心关注点:」

类上加了注解

Jmh基准测试,看我怎么用它来测试Mongodb的数据加载性能

  • 需要测试的方法用 @Benchmark 注解标识
  • 启动的方式

Jmh基准测试,看我怎么用它来测试Mongodb的数据加载性能

这些注解的具体含义将在下面介绍。

大家有兴趣可以看下官方提供的 jmh 示例 demo:http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/

  • 执行基准测试

准备工作做好了,接下来,运行代码,等待片刻,测试结果就出来了

  1. # JMH version: 1.29 
  2. # VM version: JDK 1.8.0_251, Java HotSpot(TM) Client VM, 25.251-b08 
  3. # VM invoker: C:\soft\Java\jdk1.8.0_251\jre\bin\java.exe 
  4. # VM options: -javaagent:C:\soft\idea\IntelliJ IDEA Community Edition 2020.1.1\lib\idea_rt.jar=53895:C:\soft\idea\IntelliJ IDEA Community Edition 2020.1.1\bin -Dfile.encoding=UTF-8 
  5. # Blackhole mode: full + dont-inline hint 
  6. # Warmup: 2 iterations, 5 s each 
  7. # Measurement: 10 iterations, 5 s each 
  8. # Timeout: 10 min per iteration 
  9. # Threads: 1 thread, will synchronize iterations 
  10. # Benchmark mode: Average timetime/op 
  11. # Benchmark: com.db.jmh.write.WriteBenchMarks.writeHibernate 
  12. # Parameters: (info = 10031,1,5) 
  13.  
  14. # Run progress: 0.00% complete, ETA 00:06:00 
  15. # Fork: 1 of 1 
  16. # Warmup Iteration   1: 7.743 ms/op 
  17. # Warmup Iteration   2: 9.433 ms/op 
  18. Iteration   1: 7.854 ms/op 
  19. Iteration   2: 8.638 ms/op 
  20. Iteration   3: 8.579 ms/op 
  21. Iteration   4: 8.213 ms/op 
  22. Iteration   5: 8.843 ms/op 
  23. Iteration   6: 9.178 ms/op 
  24. Iteration   7: 7.739 ms/op 
  25. Iteration   8: 9.608 ms/op 
  26. Iteration   9: 10.152 ms/op 
  27. Iteration  10: 9.461 ms/op 
  28.  
  29. Result "com.db.jmh.write.WriteBenchMarks.writeHibernate"
  30.   8.827 ±(99.9%) 1.182 ms/op [Average] 
  31.   (minavgmax) = (7.739, 8.827, 10.152), stdev = 0.782 
  32.   CI (99.9%): [7.645, 10.008] (assumes normal distribution) 
  33.  
  34. # Run complete. Total time: 00:06:38 
  35.  
  36. REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on 
  37. why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial 
  38. experiments, perform baseline and negative tests that provide experimental control, make sure 
  39. the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. 
  40. Do not assume the numbers tell you what you want them to tell. 
  41.  
  42. Benchmark                           (info)  Mode  Cnt   Score   Error  Units 
  43. WriteBenchMarks.writeHibernate   10031,1,5  avgt   10   8.827 ± 1.182  ms/op 
  44. WriteBenchMarks.writeHibernate   10032,5,6  avgt   10   8.783 ± 1.478  ms/op 
  45. WriteBenchMarks.writeHibernate  10033,5,20  avgt   10  12.574 ± 0.928  ms/op 
  46. WriteBenchMarks.writeMongo       10031,1,5  avgt   10   5.057 ± 0.358  ms/op 
  47. WriteBenchMarks.writeMongo       10032,5,6  avgt   10   7.392 ± 0.651  ms/op 
  48. WriteBenchMarks.writeMongo      10033,5,20  avgt   10  12.590 ± 0.795  ms/op 

下面对结果做下简单说明:

  1. # JMH version: 1.29 
  2. # VM version: JDK 1.8.0_251, Java HotSpot(TM) Client VM, 25.251-b08 
  3. # VM invoker: C:\soft\Java\jdk1.8.0_251\jre\bin\java.exe 
  4. # VM options: -javaagent:C:\soft\idea\IntelliJ IDEA Community Edition 2020.1.1\lib\idea_rt.jar=53895:C:\soft\idea\IntelliJ IDEA Community Edition 2020.1.1\bin -Dfile.encoding=UTF-8 
  5. # Blackhole mode: full + dont-inline hint 
  6. # Warmup: 2 iterations, 5 s each 
  7. # Measurement: 10 iterations, 5 s each 
  8. # Timeout: 10 min per iteration 
  9. # Threads: 1 thread, will synchronize iterations 
  10. # Benchmark mode: Average timetime/op 
  11. # Benchmark: com.db.jmh.write.WriteBenchMarks.writeHibernate 
  12. # Parameters: (info = 10031,1,5) 

该部分为「测试的基本信息」,比如使用的 Java 路径,预热代码的迭代次数,测量代码的迭代次数,使用的线程数量,测试的统计单位等。

  1. # Warmup Iteration   1: 7.743 ms/op 
  2. # Warmup Iteration   2: 9.433 ms/op 

该部分为每一次热身中的性能指标,预热测试不会作为最终的统计结果。预热的目的是「让 JVM 对被测代码进行足够多的优化」,比如,在预热后,被测代码应该得到了充分的 JIT 编译和优化。

  1. Iteration   1: 7.854 ms/op 
  2. Iteration   2: 8.638 ms/op 
  3. Iteration   3: 8.579 ms/op 
  4. Iteration   4: 8.213 ms/op 
  5. Iteration   5: 8.843 ms/op 
  6. Iteration   6: 9.178 ms/op 
  7. Iteration   7: 7.739 ms/op 
  8. Iteration   8: 9.608 ms/op 
  9. Iteration   9: 10.152 ms/op 
  10. Iteration  10: 9.461 ms/op 
  11.  
  12.  
  13. Result "com.db.jmh.write.WriteBenchMarks.writeHibernate"
  14.   8.827 ±(99.9%) 1.182 ms/op [Average] 
  15.   (minavgmax) = (7.739, 8.827, 10.152), stdev = 0.782 
  16.   CI (99.9%): [7.645, 10.008] (assumes normal distribution) 

该部分显示测量迭代的情况,每一次迭代都显示了当前的执行速率,即一个操作所花费的时,在进行 10 次迭代后,进行统计。

最后的测试结果如下所示:

  1. Benchmark                           (info)  Mode  Cnt   Score   Error  Units 
  2. WriteBenchMarks.writeHibernate   10031,1,5  avgt   10   8.827 ± 1.182  ms/op 
  3. WriteBenchMarks.writeHibernate   10032,5,6  avgt   10   8.783 ± 1.478  ms/op 
  4. WriteBenchMarks.writeHibernate  10033,5,20  avgt   10  12.574 ± 0.928  ms/op 
  5. WriteBenchMarks.writeMongo       10031,1,5  avgt   10   5.057 ± 0.358  ms/op 
  6. WriteBenchMarks.writeMongo       10032,5,6  avgt   10   7.392 ± 0.651  ms/op 
  7. WriteBenchMarks.writeMongo      10033,5,20  avgt   10  12.590 ± 0.795  ms/op 

看这些数据也能看出个大概,不过我不大可能直接将这个数据扔给老大, 因此用了以下两个网站

  • JMH Visual Chart:http://deepoove.com/jmh-visual-chart/
  • JMH Visualizer:https://jmh.morethan.io/

生成了一开始看到的那张图形化界面。

补充下,JMH 基础

为了能够更好地使用 JMH 的各项功能,下面对 JMH 的基本概念进行讲解:

  • @BenchmarkMode

用来配置 Mode 选项,可用于类或者方法上,这个注解的 value 是一个数组,可以把几种 Mode 集合在一起执行,如:@BenchmarkMode({Mode.SampleTime, Mode.AverageTime}),还可以设置为 Mode.All,即全部执行一遍。

  1. Throughput:整体吞吐量,每秒执行了多少次调用,单位为 ops/time
  2. AverageTime:用的平均时间,每次操作的平均时间,单位为 time/op
  3. SampleTime:随机取样,最后输出取样结果的分布
  4. SingleShotTime:只运行一次,往往同时把 Warmup 次数设为 0,用于测试冷启动时的性能
  5. All:上面的所有模式都执行一次
  • @State

通过 State 可以指定一个对象的作用范围,JMH 根据 scope 来进行实例化和共享操作。@State 可以被继承使用,如果父类定义了该注解,子类则无需定义。由于 JMH 允许多线程同时执行测试,不同的选项含义如下:

  1. Scope.Benchmark:所有测试线程共享一个实例,测试有状态实例在多线程共享下的性能
  2. Scope.Group:同一个线程在同一个 group 里共享实例
  3. Scope.Thread:默认的 State,每个测试线程分配一个实例

@OutputTimeUnit

为统计结果的时间单位,可用于类或者方法注解

  • @Warmup

预热所需要配置的一些基本测试参数,可用于类或者方法上。一般前几次进行程序测试的时候都会比较慢,所以要让程序进行几轮预热,保证测试的准确性。参数如下所示:

  1. iterations:预热的次数
  2. time:每次预热的时间
  3. timeUnit:时间的单位,默认秒
  4. batchSize:批处理大小,每次操作调用几次方法
  • @Measurement

实际调用方法所需要配置的一些基本测试参数,可用于类或者方法上,参数和 @Warmup 相同。

  • @Threads

每个进程中的测试线程,可用于类或者方法上。

  • @Fork

进行 fork 的次数,可用于类或者方法上。如果 fork 数是 2 的话,则 JMH 会 fork 出两个进程来进行测试。

  • @Param

指定某项参数的多种情况,特别适合用来测试一个函数在不同的参数输入的情况下的性能,只能作用在字段上,使用该注解必须定义 @State 注解。

在介绍完常用的注解后,让我们来看下 JMH 有哪些陷阱。

回答个疑问,为什么需要预热?

因为 JVM 的 JIT 机制的存在,如果某个函数被调用多次之后,JVM 会尝试将其编译为机器码,从而提高执行速度,所以为了让 benchmark 的结果更加接近真实情况就需要进行预热。

如何将测试结果 可视化

其实很简单,将main函数改成

  1. public static void main(String[] args) throws RunnerException { 
  2.     Options opt = new OptionsBuilder() 
  3.             .include(WriteBenchMarks.class.getSimpleName()) 
  4.             .result("db_read.json"
  5.             .resultFormat(ResultFormatType.JSON).build(); 
  6.     new Runner(opt).run(); 

就可以了,再将生成的json格式文件扔进以下网站:

  • JMH Visual Chart:http://deepoove.com/jmh-visual-chart/
  • JMH Visualizer:https://jmh.morethan.io/

Jmh基准测试,看我怎么用它来测试Mongodb的数据加载性能

就可以了啦。

「小饭饭:」 我测完啦,还生成了柱形图给你看看

「主管小肥肥:」 不错,mongodb的性能确实ok,你做的也不错,还以为你会用System.currentTimeMillis()这种low的手段呢,没想到用上了JMH,做的不错,快调薪了,必须给你加一笔。

原文链接:http://www.zzvips.com/uploads/allimg/0sxmyrkfevv

 

延伸 · 阅读

精彩推荐
  • MongoDB在mac系统下安装与配置mongoDB数据库

    在mac系统下安装与配置mongoDB数据库

    这篇文章主要介绍了在mac系统下安装与配置mongoDB数据库的操作步骤,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...

    CXYhh1219312021-11-14
  • MongoDBMongoDB查询之高级操作详解(多条件查询、正则匹配查询等)

    MongoDB查询之高级操作详解(多条件查询、正则匹配查询等)

    这篇文章主要给大家介绍了关于MongoDB查询之高级操作(多条件查询、正则匹配查询等)的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者...

    w田翔3872020-12-19
  • MongoDBMongoDB多条件模糊查询示例代码

    MongoDB多条件模糊查询示例代码

    这篇文章主要给大家介绍了关于MongoDB多条件模糊查询的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用MongoDB具有一定的参考学习价值...

    浅夏晴空5902020-05-25
  • MongoDBmongodb数据库基础知识之连表查询

    mongodb数据库基础知识之连表查询

    这篇文章主要给大家介绍了关于mongodb数据库基础知识之连表查询的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用mongodb具有一定的参...

    ZJW02155642020-05-22
  • MongoDBMongoDB的索引

    MongoDB的索引

    数据库中的索引就是用来提高查询操作的性能,但是会影响插入、更新和删除的效率,因为数据库不仅要执行这些操作,还要负责索引的更新 ...

    MongoDB教程网2532020-05-12
  • MongoDBMongoDB系列教程(五):mongo语法和mysql语法对比学习

    MongoDB系列教程(五):mongo语法和mysql语法对比学习

    这篇文章主要介绍了MongoDB系列教程(五):mongo语法和mysql语法对比学习,本文对熟悉Mysql数据库的同学来说帮助很大,用对比的方式可以快速学习到MongoDB的命...

    MongoDB教程网3252020-05-01
  • MongoDBWindows下MongoDB配置用户权限实例

    Windows下MongoDB配置用户权限实例

    这篇文章主要介绍了Windows下MongoDB配置用户权限实例,本文实现需要输入用户名、密码才可以访问MongoDB数据库,需要的朋友可以参考下 ...

    MongoDB教程网3082020-04-29
  • MongoDBMongodb索引的优化

    Mongodb索引的优化

    MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。接下来通过本文给大家介绍Mongodb索引的优化,本文介绍的非常详细,具有参考借鉴价值,感...

    MRR3252020-05-05