提交訂單效能優化系列之004-測試hikari資料來源
概括總結
使用hikari資料來源之後,相對於第003版的druid資料來源,從提交訂單
這個複雜的操作上來說,效能提升了17.79%。而從獲取資料庫連線
這一簡單的操作上來說,hikari比druid優秀幾百倍。
004版本更新說明
pom.xml檔案中引入了新的jar包:
<!-- HikariCP資料庫連線工具 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version >3.2.0</version>
</dependency>
HikariCP-3.2.0.jar
包的大小是141KB,而003版本中的druid-1.1.10.jar
包的大小是2693KB,甚至都不是一個數量級的。
這一版本寫了兩個測試類:Version004Test
和Version004Test2
。其中Version004Test
用於測試提交訂單的耗時情況,Version004Test2
用於測試獲取資料庫連線的耗時情況。另外,由於hikari的效能好得超乎想像,毫秒
這個單位已經太粗糙了,於是把Version004Test2
中的時間單位改成了納秒
。
測試結果
Version004Test
002版本(自己寫的資料來源)耗時(ms) | 003版本(druid資料來源)耗時(ms) | 004版本(hikari資料庫源)耗時(ms) |
---|---|---|
1047 | 1259 | 1035 |
003版本相對於002版本效能下降了: (1259 - 1047) / 1047 * 100% = 20.24%
004版本相對於003版本效能提升了: (1259 - 1035) / 1259 * 100% = 17.79%
004版本相對於002版本效能提升了: (1047 - 1035) / 1047 * 100% = 1.14%
Version004Test2
的測試結果:
日誌列印如下:
druid 獲取一個數據庫連線平均耗時:2392283 納秒(獲取連線後不關閉連線) hikari 獲取一個數據庫連線平均耗時:3268791 納秒(獲取連線後不關閉連線) druid 獲取一個數據庫連線平均耗時:1812491 納秒(獲取連線後不關閉連線) hikari 獲取一個數據庫連線平均耗時:61716 納秒(獲取連線後不關閉連線) druid 獲取一個數據庫連線平均耗時:1800770 納秒(獲取連線後關閉連線) hikari 獲取一個數據庫連線平均耗時:2175 納秒(獲取連線後關閉連線) druid 獲取一個數據庫連線平均耗時:1834128 納秒(獲取連線後關閉連線) hikari 獲取一個數據庫連線平均耗時:2614 納秒(獲取連線後關閉連線)
可以總結為:如果獲取連線後,不關閉連線,則druid和hikari在效能上是一個級別的。但是由於實際專案中,獲取連線後都會關閉連線,這時候hikari的效能比druid優秀幾百倍:1800770 / 2175 = 827.94 倍。
【備註】:不同的機器上的測試結果會不一樣,以上測試結果僅供參考。
hikari為什麼這麼優秀?
HikariCP自己宣傳的賣點是”zero-overhead“,翻譯成中文也就是”零開銷“。但是要注意它是打了引號的,意思可以理解為:雖然不是真的零開銷,但是跟其他的資料來源工具比起來,可以認為是零開銷了。這樣的宣傳我是可以接受的。
這可不像是druid那樣明目張膽地吹牛皮說自己是Java語言中最好的資料庫連線池,連引號都不打。這樣的宣傳我也是可以接受的,畢竟druid在github上的星星數量確實是比其他的資料來源高很多。
話說回來,hikari為什麼這麼優秀呢?
這個回答我在測試之前點進去過,一看是英文版的,馬上關閉了。後來看到了Version004Test2
的測試結果,又打開了那個頁面,靠著有道詞典一字一句地把文章看完了。總結起來有以下兩點:
ArrayList的優化:當從ArrayList中移除某個物件時,會
從頭到尾
執行掃描,但是我們知道,資料庫相關的物件有很多都是需要關閉
的,關閉的順序一般是與開啟的順序相反的,即最需要關閉的物件是儲存在列表的尾部的,這意味著,如果能從尾到頭
來掃描的話,效能就會有提升。於是ArrayList就被自定義的FastList取代了。位元組碼層面的優化:對比一下以下兩行程式碼的區別:
程式碼段1:(其中
PROXY_FACTORY
是類ProxyFactory
的一個靜態例項物件)PROXY_FACTORY.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnNames));
程式碼段2:(其中ProxyFactory
是一個類)ProxyFactory.getProxyPreparedStatement(this, delegate.prepareStatement(sql, columnNames));
你能看出誰比誰的效能更高嗎?反正我是看不出來。不過,雖然從java程式碼的層面看不出來,但是一旦編譯成了位元組碼,區別就很明顯了(第二種效能更好)。我在這裡就不列舉了,你可以去這裡看看。
當然了,這兩點只是樣本例子,並不代表這個工具包中只做了這兩點優化。特別是其中的第2點,能涵蓋的範圍非常廣泛,真是讓人佩服。