記錄Redis事故影響API性能-上篇

背景

  • 線上服務報大量的Redis,相關依賴這個服務的其餘產品線服務也報出一樣的日誌。
"Could not get a resource from the pool  org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool" 和 "Connect timed out」 
複製代碼
  • 截圖線上日誌
    image

解決方法:

  • 沒有上線/相關請求的服務的流量和QPS都沒有增長,故認爲是redis 服務的問題,聯繫咱們運維查相關服務是否有異常。進過排查重啓了一臺哨兵服務節點,但不影響服務。
  • 堅定認爲是服務有問題並認爲是某個節點的問題,嘗試修改配置中redis 哨兵節點數來校驗是否是redis 哨兵有問題,在咱們內部雲平臺中操做 修改配置並重啓服務,還由於這個操做致使了商城一次15分鐘的商城故障【得不償失】。
  • 通過兩天 從查看代碼,分析相關日誌,諮詢運維redis監控服務是否有相關問題,最後從redis 的慢查詢監控有大量的keys 慢查詢,最終肯定是keys 致使的,修改代碼並在假日緊急上線,問題修復。

分析

爲何redis 的keys 會致使服務拿不到資源,而且咱們有連接池,咱們帶着這樣的問題,咱們來剖析一下java

  • redis是單進程單線程實現的,若是你沒有特殊的配置,redis內部默認是FIFO排隊,即你對redis的訪問都是要在redis進行排隊,先入先出的串行執行。

image

  • 經過分析 Jedis 內部的pom 來得知 jedis 的連接池的實現是基於common-pool2的連接池來實現的

image

從Jedis 中jar 包查看有三種連接池, 選擇哪一個連接池取決於你的Redis的環境redis

  • JedisPool-單節點模式
  • JedisSentinelPool - 哨兵模式
  • ShardedJedisPool-集羣共享模式

咱們的服務用的哨兵模式,對於這幾種的redis 連接池的實現方式 請下載源碼自行查看,這塊不是這篇文章的重點算法

  • 咱們經過下面的圖來看一下連接池的建立基本流程spring

    看這個圖以前請先了解 common-pool2 實現池的概念和相關參數的解釋【下次有時間我在講解一下這塊的源碼】數據庫

    image

  • 先展現一下咱們系統的相關配置 和 這些配置對應的解釋網絡

    image

找根因

因爲 redis單進程單線程的模式,當咱們使用keys 的時候查詢很慢的時候,勢必會阻塞後面client鏈接從而報獲取不到資源, 這裏面的前提條件是通過覈實 redis 的鏈接數是沒有達到咱們設置的鏈接池的最大數的併發

​ 這裏面有個最大的疑問是,我都已經有鏈接池而且沒有達到你設置的最大的鏈接池數量,爲何報拿不到鏈接池的資源或則是鏈接超時呢,其實這裏面最關鍵的配置是 max-idle和max-wait,so way, 其實經過上面的鋪墊的知識就能夠得出 redis 是單進程單線程 而且默認是FIFO排隊機制,當個人最大空閒資源耗盡的時候須要經過TCP 方式 和 Redis Server 創建鏈接,同時因爲keys 操做很耗時 因此這個建立在阻塞的而且當達到咱們max-wait 就會返回 獲取不到鏈接池的資源或則是鏈接超時。運維

擴展

  1. 經過上面的結論 咱們是否能夠認爲 調整 max-idle 和 max-wait 的設置 就能夠解決這個問題,這個答案是否認的,只有解決性能問題纔是最終解決方案。tcp

  2. Redis 這種特性用不用鏈接池都同樣?post

    雖然基於內存的Redis數據庫有着超高的性能,可是底層的網絡通訊卻佔用了一次數據請求的大量時間,由於每次數據交互都須要先創建鏈接,假設一次數據交互總共用時30ms,超高性能的Redis數據庫處理數據所花的時間可能不到1ms,也便是說前期的鏈接佔用了29ms,jedis直連方式,也就是每次new jedis都會新建tcp鏈接,使用後再斷開鏈接,這對於頻繁訪問redis的場景顯然不是高效的使用方式。鏈接池則能夠實如今客戶端創建多個連接而且不釋放,當須要使用鏈接的時候經過必定的算法獲取已經創建的鏈接,使用完了之後則還給鏈接池,這就免去了數據庫鏈接所佔用的時間。所以,一般會使用鏈接池的方式對Jedis鏈接進行管理,全部jedis對象會預先放在池子中(JedisPool),每次要鏈接redis,只須要在池子中借,用完了再歸還給池子。客戶端鏈接Redis使用的是TCP協議,直連的方式每次須要創建TCP鏈接,而鏈接池的方式是能夠預先初始化好jedis鏈接,每次只須要從jedis鏈接池借用便可,借用和歸還操做是在本地進行的,只有少許的併發同步開銷,遠遠小於新建tcp鏈接的開銷。此外,鏈接池的方式能夠有效保護和控制資源的使用,而直連的方式沒法限制jedis對象的個數,而且可能存在鏈接泄漏的狀況。

  3. 對於咱們鏈接哨兵模式 若是某一個節點掛掉了會不會出現 獲取不到鏈接池和超時呢?且聽下回分解

關聯文章

​ 《記錄Redis事故影響API性能-下篇》

做者:易企秀工程師 henry_chen

相關文章
相關標籤/搜索