長期處於CRUD工做中的我忽然有一天關心起本身項目的qps
了.便用jmeter
測試了訪問量最大的接口,發現只有可憐的17r/s
左右......看到網絡上比比皆是的幾百萬qps
,我無地自容啊.java
因而就準備進行性能優化了,不過在優化前咱們須要進行性能測試,這樣才能知道優化的效果如何.好比我第一步就是用redis
加了緩存,結果測試發現竟然比不加以前還要慢???因此性能測試是很是重要的一環,而jmh
就是很是適合的性能測試工具了.redis
準備工做很是的簡單,引入jmh
的maven
包就能夠了.spring
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.22</version>
<scope>provided</scope>
</dependency>
複製代碼
package jmh; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; /** * Benchmark * * @author wangpenglei * @since 2019/11/27 13:54 */ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MILLISECONDS) @State(Scope.Benchmark) public class Benchmark { public static void main(String[] args) throws Exception { // 使用一個單獨進程執行測試,執行5遍warmup,而後執行5遍測試 Options opt = new OptionsBuilder().include(Benchmark.class.getSimpleName()).forks(1).warmupIterations(5) .measurementIterations(5).build(); new Runner(opt).run(); } @Setup public void init() { } @TearDown public void down() { } @org.openjdk.jmh.annotations.Benchmark public void test() { } } 複製代碼
這個註解決定了測試模式,具體的內容網絡上很是多,我這裏採用的是計算平均運行時間數據庫
這個註解是最後輸出結果時的單位.由於測試的是接口,因此我採用的是毫秒.若是是測試本地redis
或者本地方法這種能夠換更小的單位.緩存
這個註解定義了給定類實例的可用範圍,由於spring
裏的bean
默認是單例,因此我這裏採用的是運行相同測試的全部線程將共享實例。能夠用來測試狀態對象的多線程性能(或者僅標記該範圍的基準).性能優化
很是簡單的註解,日常測試都有的測試前初始化*測試後清理資源**測試方法*.bash
由於咱們須要spring
的環境才能測試容器裏的bean
,因此須要在初始化方法中手動建立一個.我查了一下資料沒發現什麼更好的方法,就先本身手動建立吧.服務器
private ConfigurableApplicationContext context; private AppGoodsController controller; @Setup public void init() { // 這裏的WebApplication.class是項目裏的spring boot啓動類 context = SpringApplication.run(WebApplication.class); // 獲取須要測試的bean this.controller = context.getBean(AppGoodsController.class); } @TearDown public void down() { context.close(); } 複製代碼
寫好測試方法後啓動main
方法就開始測試了,如今會報一些奇奇怪怪的錯誤,不過不影響結果我就沒管了.運行完成後會輸出結果,這時候能夠對比下優化的效果.markdown
Result "jmh.Benchmark.testGetGoodsList": 65.969 ±(99.9%) 10.683 ms/op [Average] (min, avg, max) = (63.087, 65.969, 69.996), stdev = 2.774 CI (99.9%): [55.286, 76.652] (assumes normal distribution) # Run complete. Total time: 00:02:48 REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial experiments, perform baseline and negative tests that provide experimental control, make sure the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. Do not assume the numbers tell you what you want them to tell. Benchmark Mode Cnt Score Error Units Benchmark.testGetGoodsList avgt 5 65.969 ± 10.683 ms/op Process finished with exit code 0 複製代碼
在文章開頭我講了一個負優化的例子,我用redis
加了緩存後竟然比直接數據庫查詢還要慢!其實緣由很簡單,我在本地電腦上測試,鏈接的redis
卻部署在服務器上.這樣來回公網的網絡延遲就已經很大了.不過數據庫也是經過公網的,也不會比redis
快纔對.最後的緣由是發現部署redis
的服務器帶寬只有1m
也就是100kb/s
,很容易就被佔滿了.最後優化是redis
加緩存與使用內網鏈接redis
.網絡
Result "jmh.Benchmark.testGetGoodsList": 102.419 ±(99.9%) 153.083 ms/op [Average] (min, avg, max) = (65.047, 102.419, 162.409), stdev = 39.755 CI (99.9%): [≈ 0, 255.502] (assumes normal distribution) # Run complete. Total time: 00:03:03 REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial experiments, perform baseline and negative tests that provide experimental control, make sure the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. Do not assume the numbers tell you what you want them to tell. Benchmark Mode Cnt Score Error Units Benchmark.testGetGoodsList avgt 5 102.419 ± 153.083 ms/op Process finished with exit code 0 複製代碼
redis
速度,連的是本地redis
):Result "jmh.Benchmark.testGetGoodsList": 29.210 ±(99.9%) 2.947 ms/op [Average] (min, avg, max) = (28.479, 29.210, 30.380), stdev = 0.765 CI (99.9%): [26.263, 32.157] (assumes normal distribution) # Run complete. Total time: 00:02:49 REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial experiments, perform baseline and negative tests that provide experimental control, make sure the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. Do not assume the numbers tell you what you want them to tell. Benchmark Mode Cnt Score Error Units Benchmark.testGetGoodsList avgt 5 29.210 ± 2.947 ms/op Process finished with exit code 0 複製代碼
能夠看到大約快了3.5
倍,其實還有優化空間,所有數據庫操做都經過redis
緩存的話,大概1ms
就處理完了.