HikariCP連接池監控指標實戰

業務方關注哪些數據庫指標?

首先分享一下自己之前的一段筆記(找不到引用出處了)

  • 系統中多少個線程在進行與數據庫有關的工作?其中,而多少個線程正在執行 SQL 語句?這可以讓我們評估數據庫是不是系統瓶頸。

  • 多少個線程在等待獲取數據庫連接?獲取數據庫連接需要的平均時長是多少?數據庫連接池是否已經不能滿足業務模塊需求?如果存在獲取數據庫連接較慢,如大於 100ms,則可能說明配置的數據庫連接數不足,或存在連接洩漏問題。

  • 哪些線程正在執行 SQL 語句?執行了的 SQL 語句是什麼?數據庫中是否存在系統瓶頸或已經產生鎖?如果個別 SQL 語句執行速度明顯比其它語句慢,則可能是數據庫查詢邏輯問題,或者已經存在了鎖表的情況,這些都應當在系統優化時解決。

  • 最經常被執行的 SQL 語句是在哪段源代碼中被調用的?最耗時的 SQL 語句是在哪段源代碼中被調用的?在浩如煙海的源代碼中找到某條 SQL 並不是一件很容易的事。而當存在問題的 SQL 是在底層代碼中,我們就很難知道是哪段代碼調用了這個 SQL,併產生了這些系統問題。

在研究HikariCP的過程中,這些業務關注點我發現在連接池這層逐漸找到了答案。


監控指標

HikariCP指標說明類型備註
hikaricp_connection_timeout_total每分鐘超時連接數Counter
hikaricp_pending_threads當前排隊獲取連接的線程數GAUGE關鍵指標,大於10則 報警
hikaricp_connection_acquired_nanos連接獲取的等待時間Summarypool.Wait 關注99極值
hikaricp_active_connections當前正在使用的連接數GAUGE
hikaricp_connection_creation_millis創建連接成功的耗時Summary關注99極值
hikaricp_idle_connections當前空閒連接數GAUGE關鍵指標,默認10,因為降低為0會大大增加連接池創建開銷
hikaricp_connection_usage_millis連接被複用的間隔時長
Summarypool.Usage 關注99極值
hikaricp_connections連接池的總共連接數GAUGE

重點關注

hikaricp_pending_threads

該指標持續飆高,說明DB連接池中基本已無空閒連接。

拿之前業務方應用pisces不可用的例子來說(如下圖所示),當時所有線程都在排隊等待,該指標已達172,此時調用方已經產生了大量超時及熔斷,雖然業務方沒有馬上找到拿不到連接的根本原因,但是這個告警出來之後及時進行了重啟,避免產生更大的影響。

hikaricp_connection_acquired_nanos(取99位數)

下圖是Hikari源碼com.zaxxer.hikari.pool.HikariPool#getConnection部分,

 public Connection getConnection(final long hardTimeout) throws SQLException { suspendResumeLock.acquire(); final long startTime = currentTime(); try { long timeout = hardTimeout; do { PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS); if (poolEntry == null) { break; // We timed out... break and throw exception } final long now = currentTime(); if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > ALIVE_BYPASS_WINDOW_MS && !isConnectionAlive(poolEntry.connection))) { closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE); timeout = hardTimeout - elapsedMillis(startTime); } else { metricsTracker.recordBorrowStats(poolEntry, startTime); return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now); } } while (timeout > 0L); metricsTracker.recordBorrowTimeoutStats(startTime); throw createTimeoutException(startTime); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new SQLException(poolName + " - Interrupted during connection acquisition", e); } finally { suspendResumeLock.release(); } }

從上述代碼可以看到,suspendResumeLock.acquire()走到poolEntry == null時已經超時了,拿到一個poolEntry後先判斷是否已經被標記為待清理或已經超過了設置的最大存活時間(應用配置的最大存活時間不應超過DBA在DB端配置的最大連接存活時間),若是直接關閉繼續調用borrow,否則才會返回該連接,metricsTracker.recordBorrowTimeoutStats(startTime);該段代碼的意義就是此指標的記錄處。

Vesta模版中該指標單位配為了毫秒,此指標和排隊線程數結合,可以初步提出 增大連接數優化慢查詢/慢事務 的優化方案等。

  • 排隊線程數多獲取連接的耗時較短 時,可以考慮增大連接數

  • 排隊線程數少獲取連接的耗時較長 時,此種場景不常見,舉例來說,可能是某個接口QPS較低,連接數配的小於這個QPS,而這個連接中有較慢的查詢或事務,這個需要具體問題具體分析

  • 排隊線程數多獲取連接的耗時較長時,這種場景比較危險,有可能是某個時間點DB壓力大或者網絡抖動造成的,排除這些場景,若長時間出現這種情況則可認為

    連接配置不合理/程序是沒有達到上線標準 ,如果可以從業務邏輯上優化慢查詢/慢事務是最好的,否則可以嘗試 增大連接數應用擴容

hikaricp_idle_connections

Hikari是可以配置最小空閒連接數的,當此指標長期比較高(等於最大連接數)時,可以適當減小配置項中最小連接數。

hikaricp_active_connections

此指標長期在設置的最大連接數上下波動時,或者長期保持在最大線程數時,可以考慮增大最大連接數。

hikaricp_connection_usage_millis(取99位數)

該配置的意義在於表明 連接池中的一個連接從 被返回連接池再被複用 的時間間隔,對於使用較少的數據源,此指標可能會達到秒級,可以結合流量高峰期的此項指標與激活連接數指標來確定是否需要減小最小連接數,若高峰也是秒級,說明對比數據源使用不頻繁,可考慮減小連接數。

hikaricp_connection_timeout_total

該配置的意義在於表明 連接池中總共超時的連接數量,此處的超時指的是連接創建超時。經常連接創建超時,一個排查方向是和運維配合檢查下網絡是否正常。

hikaricp_connection_creation_millis(取99位數)

該配置的意義在於表明 創建一個連接的耗時。主要反映當前機器到數據庫的網絡情況,在IDC意義不大,除非是網絡抖動或者機房間通訊中斷才會有異常波動。

監控指標部分實戰案例

以下連接風暴和慢SQL兩種場景是可以採用HikariCP連接池監控的。

連接風暴

連接風暴,也可稱為網絡風暴,當應用啟動的時候,經常會碰到各應用服務器的連接數異常飆升,這是大規模應用集群很容易碰到的問題。先來描述一個場景

在項目發佈的過程中,我們需要重啟應用,當應用啟動的時候,經常會碰到各應用服務器的連接數異常飆升。假設連接數的設置為:min值3,max值10。正常的業務使用連接數在5個左右,當重啟應用時,各應用連接數可能會飆升到10個,瞬間甚至還有可能部分應用會報取不到連接。啟動完成後接下來的時間內,連接開始慢慢返回到業務的正常值。這種場景,就是碰到了所謂的連接風暴。

連接風暴可能帶來的危害主要有:

  • 在多個應用系統同時啟動時,系統大量佔用數據庫連接資源,可能導致數據庫連接數耗盡

  • 數據庫創建連接的能力是有限的,並且是非常耗時和消耗CPU等資源的,突然大量請求落到數據庫上,極端情況下可能導致數據庫異常crash。

  • 對於應用系統來說,多一個連接也就多佔用一點資源。在啟動的時候,連接會填充到max值,並有可能導致瞬間業務請求失敗。

與連接風暴類似的還有:

  • 啟動時的preparedstatement風暴

  • 緩存穿透。在緩存使用的場景中,緩存KEY值失效的風暴(單個KEY值失效,PUT時間較長,導致穿透緩存落到DB上,對DB造成壓力)。可以採用

    布隆過濾器單獨設置個緩存區域存儲空值,對要查詢的key進行預先校驗緩存降級等方法。

  • 緩存雪崩。上條的惡化,所有原本應該訪問緩存的請求都去查詢數據庫了,而對數據庫CPU和內存造成巨大壓力,嚴重的會造成數據庫宕機。從而形成一系列連鎖反應,造成整個系統崩潰。可以採用 加鎖排隊設置過期標誌更新緩存設置過期標誌更新緩存二級緩存(引入一致性問題)預熱緩存與服務降級等解決方法。

案例一 某公司訂單業務(劉龘劉同學提供):

我們那時候採用彈性伸縮,數據庫連接池是默認的,有點業務出了點異常,導致某個不重要的業務彈出N臺機器,導致整個數據庫連接不可用,影響訂單主業務。

該案例就可以理解為是一次連接風暴,當時剛好那個服務跟訂單合用一個數據庫了,訂單服務只能申請到默認連接數,訪問訂單TPS上不去,老劉同學說“損失慘重才能刻骨銘心呀”。

案例二 切庫(DBA提供):

我司在切庫的時候產生過連接風暴,瞬間所有業務全部斷開重連,造成連接風暴,暫時通過加大連接數解決此問題。當然,單個應用重啟的時候可以忽略不計,因為,一個庫的依賴服務不可能同時重啟。

案例三 機房出故障(DBA提供):

以前機房出故障的時候,應用全部湧進來,有過一次連接炸掉的情況。

慢SQL

我司的瓶頸其實不在連接風暴,我們的併發並不是很高,和電商不太一樣。複雜 SQL 很多,清算、對賬的複雜SQL都不少,部分業務的SQL比較複雜。比如之前有過一次催收線上故障,就是由於慢SQL導致Hikari連接池佔滿,排隊線程指標飆升,當時是無法看到整個連接池的歷史趨勢的,也很難看到連接池實時指標,有了本監控大盤工具之後,業務方可以更方便得排查類似問題。

如何調優

經驗配置連接池參數及監控告警

首先分享一個小故事《扁鵲三兄弟》

春秋戰國時期,有位神醫被尊為“醫祖”,他就是“扁鵲”。一次,魏文王問扁鵲說:“你們家兄弟三人,都精於醫術,到底哪一位最好呢?”扁鵲答:“長兄最好,中兄次之,我最差。”文王又問:“那麼為什麼你最出名呢?”扁鵲答:“長兄治病,是治病於病情發作之前,由於一般人不知道他事先能剷除病因,所以他的名氣無法傳出去;中兄治病,是治病於病情初起時,一般人以為他只能治輕微的小病,所以他的名氣只及本鄉里;而我是治病於病情嚴重之時,一般人都看到我在經脈上穿針管放血,在皮膚上敷藥等大手術,所以以為我的醫術高明,名氣因此響遍全國。”

正文羅列的幾種監控項可以配上告警,這樣,能夠在業務方發現問題之前第一時間發現問題,這就是扁鵲三兄弟大哥、二哥的做法,我們正是要努力成為扁鵲的大哥、二哥那樣的人。

根據日常的運維經驗,大多數線上應用可以使用如下的Hikari的配置:

maximumPoolSize: 20minimumIdle: 10connectionTimeout: 30000idleTimeout: 600000maxLifetime: 1800000

連接池連接數有動態和靜態兩種策略。動態即每隔一定時間就對連接池進行檢測,如果發現連接數量小於最小連接數,則補充相應數量的新連接以保證連接池的正常運轉。靜態是發現空閒連接不夠時再去檢查。這裡提一下minimumIdle,hikari實際上是不推薦用戶去更改Hikari默認連接數的。

This property controls the minimum number of idle connections that HikariCP tries to maintain in the pool. If the idle connections dip below this value and total connections in the pool are less than maximumPoolSize, HikariCP will make a best effort to add additional connections quickly and efficiently. However, for maximum performance and responsiveness to spike demands, we recommend not setting this value and instead allowing HikariCP to act as a fixed size connection pool. Default: same as maximumPoolSize

該屬性的默認值為10,Hikari為了追求最佳性能和相應尖峰需求,hikari不希望用戶使用動態連接數,因為動態連接數會在空閒的時候減少連接、有大量請求過來會創建連接,但是但是創建連接耗時較長會影響RT。還有一個考慮就是隱藏風險,比如平時都是空載的 10個機器就是100個連接,其實數據庫最大連接數比如是150個,等滿載的時候就會報錯了,這其實就是關閉動態調節的能力,跟 jvm 線上 xmx和xms 配一樣是一個道理。動態調節不是完全沒用,比如不同服務連一個db然後,業務高峰是錯開的,這樣的情況其實比較少。

更多配置解析請參見本系列第二篇《【追光者系列】HikariCP連接池配置項》

壓測

連接池的分配與釋放,對系統的性能有很大的影響。合理的分配與釋放,可以提高連接的複用度,從而降低建立新連接的開銷,同時還可以加快用戶的訪問速度。

連接池的大小設置多少合適呢?再分配多了效果也不大,一個是應用服務器維持這個連接數需要內存支持,並且維護大量的連接進行分配使用對cpu也是一個不小的負荷,因此不宜太大,雖然sleep線程增多對DBA來說目前線上已經可以忽略,但是能處理一下當然最好。如果太小,那麼在上述規模項目的併發量以及數據量上來以後會造成排隊現象,系統會變慢,數據庫連接會經常打開和關閉,性能上有壓力,用戶體驗也不好。

如何評估數據庫連接池的性能是有專門的算法公式的,【追光者系列】後續會更新,不過經驗值一般沒有壓測準,連接池太大、太小都會存在問題。具體設置多少,要看系統的訪問量,可通過反覆測試,找到最佳點。

連接風暴問題的另一種探索

對於連接風暴,如果採用傳統的proxy模式可以處理好這種問題,主要還是mysql的bio模型不支持大量連接。負載均衡

故障轉移服務自動擴容 都可以在這一層實現。


分享到:


相關文章: