MySQL的MaxIdleConns不合理,會變成短連接

1 背景

最近石墨文檔線上業務出現了一些性能問題,在突發流量情況下,有個業務性能急劇下降。該服務是依賴於數據庫的業務,會批量獲取數據庫裏的數據。在經過一系列的排查過程後,發現該服務到數據庫的連接數經常超過MaxIdleConns,因此懷疑是數據庫的配置導致的性能問題,所以以下針對數據庫的代碼進行了剖析,並做了相關實驗。

2 配置解讀

maxIdleCount      int                    // zero means defaultMaxIdleConns; negative means 0
maxOpen           int                    // <= 0 means unlimited
maxLifetime       time.Duration          // maximum amount of time a connection may be reused
maxIdleTime       time.Duration          // maximum amount of time a connection may be idle before being closed

可以看到以上四個配置,是我們Go MySQL客戶端最重要的配置。

  • maxIdleCount 最大空閒連接數,默認不配置,是2個最大空閒連接

  • maxOpen 最大連接數,默認不配置,是不限制最大連接數

  • maxLifetime 連接最大存活時間

  • maxIdleTime 空閒連接最大存活時間

3 源碼解析

我們的場景是客戶端與MySQL建立的連接數經常大於最大空閒連接數,這會導致什麼問題?我們看下下圖中的源碼。

我們可以看到,當最大空閒連接數小於客戶端與數據庫建立的連接數的時候,那麼就會返回false,並且最大連接數關閉計數器加1。

然後上圖中,我們就可以看到,連接被關閉了(MySQL源碼裏也不留點緩衝時間再關閉)。Go的MySQL客戶端這個操作,就會導致當突發流量情況下,由於請求量級過大,超過了最大空閒連接數的負載,那麼新的連接在放入連接池的時候,會被無情的關閉,變成短連接,導致你的服務性能進一步惡化。

4 實驗

4.1 模擬線上併發數大於MaxIdConns情況

測試代碼 , 爲了檢測以上邏輯,假設了以下場景,設置最大連接數爲100,最大空閒連接數爲1,併發數爲10的goroutine來請求數據庫。我們通過MySQL的stats的maxIdleClosed的統計,可以看到下圖,我們的連接不停的被關閉。

4.2 模擬線上併發數小於MaxIdConns情況

測試代碼 ,假設了以下場景,設置最大連接數爲100,最大空閒連接數爲20,併發數爲10的goroutine來請求數據庫,可以看到下圖中,無MaxIdleClosed的關閉統計。

4.3 抓包驗證線上併發數大於MaxIdConns情況

測試代碼 ,爲了驗證沒有理解錯代碼,抓個包最穩妥。我們將main函數裏放個select{},程序執行完mysql的語句後,看下tcp狀態和抓包數據。

可以發現確實是tcp的狀態統計與MySQL客戶端的統計是一致的,並且存在fin包。

5 總結

當突發流量情況下,由於請求量級過大,超過了最大空閒連接數的負載,那麼新的連接在放入連接池的時候,會被關閉,將連接變成短連接,導致服務性能進一步惡化。爲了避免這種情況,下面列舉了,可以優化的措施。

  • 提前將maxIdleConns設大,避免出現短連接

  • 做好mysql讀寫分離

  • 提升mysql的吞吐量:精簡返回字段,沒必要的字段不要返回,能夠夠快複用連接

  • 吞吐量的包儘量不要太大,避免分包

  • 優化連接池,當客戶端到MySQL的連接數大於最大空閒連接的時候,關閉能夠做一下延遲(官方不支持,估計只能自己實現)

  • 讀請求的最好不要放MySQL裏,儘量放redis裏

6 測試代碼

  • https://github.com/gotomicro/test/tree/main/gorm

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章