JedisPool資源池優(yōu)化方法
背景
合理的JedisPool資源池參數(shù)設(shè)置能為業(yè)務(wù)使用Redis保駕護航,本文將對JedisPool的使用、資源池的參數(shù)進行詳細(xì)說明,最后給出“最合理”配置。
一、使用方法
以官方的2.9.0為例子(Jedis Release),Maven依賴如下:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> <scope>compile</scope> </dependency>
Jedis使用apache commons-pool2對Jedis資源池進行管理,所以在定義JedisPool時一個很重要的參數(shù)就是資源池GenericObjectPoolConfig,使用方式如下,其中有很多資源管理和使用的參數(shù)(具體看第二節(jié))
注意:后面會提到建議用JedisPoolConfig代替GenericObjectPoolConfig
GenericObjectPoolConfig jedisPoolConfig = new GenericObjectPoolConfig(); jedisPoolConfig.setMaxTotal(..); jedisPoolConfig.setMaxIdle(..); jedisPoolConfig.setMinIdle(..); jedisPoolConfig.setMaxWaitMillis(..);
JedisPool的初始化如下:
// redisHost和redisPort是實例的IP和端口 // redisPassword是實例的密碼 // timeout,這里既是連接超時又是讀寫超時,從Jedis 2.8開始有區(qū)分connectionTimeout和soTimeout的構(gòu)造函數(shù) JedisPool jedisPool = new JedisPool(jedisPoolConfig, redisHost, redisPort, timeout, redisPassword); Jedis jedis = null; try { jedis = jedisPool.getResource(); //具體的命令 jedis.executeCommand() } catch (Exception e) { logger.error(e.getMessage(), e); } finally { if (jedis != null) jedis.close(); //注意這里不是關(guān)閉連接,在JedisPool模式下,Jedis會被歸還給資源池。 }
二、參數(shù)說明
JedisPool保證資源在一個可控范圍內(nèi),并且提供了線程安全,但是一個合理的GenericObjectPoolConfig配置能為應(yīng)用使用Redis保駕護航,下面將對它的一些重要參數(shù)進行說明和建議:
在當(dāng)前環(huán)境下,Jedis連接就是資源,JedisPool管理的就是Jedis連接。
1. 資源設(shè)置和使用
序號 | 參數(shù)名 | 含義 | 默認(rèn)值 | 使用建議 |
---|---|---|---|---|
1 | maxTotal | 資源池中最大連接數(shù) | 8 | 設(shè)置建議見下節(jié) |
2 | maxIdle | 資源池允許最大空閑的連接數(shù) | 8 | 設(shè)置建議見下節(jié) |
3 | minIdle | 資源池確保最少空閑的連接數(shù) | 0 | 設(shè)置建議見下節(jié) |
4 | blockWhenExhausted | 當(dāng)資源池用盡后,調(diào)用者是否要等待。只有當(dāng)為true時,下面的maxWaitMillis才會生效 | true | 建議使用默認(rèn)值 |
5 | maxWaitMillis | 當(dāng)資源池連接用盡后,調(diào)用者的最大等待時間(單位為毫秒) | -1:表示永不超時 | 不建議使用默認(rèn)值 |
6 | testOnBorrow | 向資源池借用連接時是否做連接有效性檢測(ping),無效連接會被移除 | false | 業(yè)務(wù)量很大時候建議設(shè)置為false(多一次ping的開銷)。 |
7 | testOnReturn | 向資源池歸還連接時是否做連接有效性檢測(ping),無效連接會被移除 | false | 業(yè)務(wù)量很大時候建議設(shè)置為false(多一次ping的開銷)。 |
8 | jmxEnabled | 是否開啟jmx監(jiān)控,可用于監(jiān)控 | true | 建議開啟,但應(yīng)用本身也要開啟 |
2.空閑資源監(jiān)測
空閑Jedis對象檢測,下面四個參數(shù)組合來完成,testWhileIdle是該功能的開關(guān)。
序號 | 參數(shù)名 | 含義 | 默認(rèn)值 | 使用建議 |
---|---|---|---|---|
1 | testWhileIdle | 是否開啟空閑資源監(jiān)測 | false | true |
2 | timeBetweenEvictionRunsMillis | 空閑資源的檢測周期(單位為毫秒) | -1:不檢測 | 建議設(shè)置,周期自行選擇,也可以默認(rèn)也可以使用下面JedisPoolConfig中的配置 |
3 | minEvictableIdleTimeMillis | 資源池中資源最小空閑時間(單位為毫秒),達(dá)到此值后空閑資源將被移除 | 1000 60 30 = 30分鐘 | 可根據(jù)自身業(yè)務(wù)決定,大部分默認(rèn)值即可,也可以考慮使用下面JeidsPoolConfig中的配置 |
4 | numTestsPerEvictionRun | 做空閑資源檢測時,每次的采樣數(shù) | 3 | 可根據(jù)自身應(yīng)用連接數(shù)進行微調(diào),如果設(shè)置為-1,就是對所有連接做空閑監(jiān)測 |
為了方便使用,Jedis提供了JedisPoolConfig,它本身繼承了GenericObjectPoolConfig設(shè)置了一些空閑監(jiān)測設(shè)置
public class JedisPoolConfig extends GenericObjectPoolConfig { public JedisPoolConfig() { // defaults to make your life with connection pool easier :) setTestWhileIdle(true); // setMinEvictableIdleTimeMillis(60000); // setTimeBetweenEvictionRunsMillis(30000); setNumTestsPerEvictionRun(-1); } }
所有默認(rèn)值可以從org.apache.commons.pool2.impl.BaseObjectPoolConfig中看到。
三、資源池大小(maxTotal)、空閑(maxIdle minIdle)設(shè)置建議
1.maxTotal:最大連接數(shù)
實際上這個是一個很難回答的問題,考慮的因素比較多:
- 業(yè)務(wù)希望Redis并發(fā)量
- 客戶端執(zhí)行命令時間
- Redis資源:例如 nodes(例如應(yīng)用個數(shù)) * maxTotal 是不能超過redis的最大連接數(shù)。
- 資源開銷:例如雖然希望控制空閑連接,但是不希望因為連接池的頻繁釋放創(chuàng)建連接造成不必靠開銷。
以一個例子說明,假設(shè):
- 一次命令時間(borrow|return resource + Jedis執(zhí)行命令(含網(wǎng)絡(luò)) )的平均耗時約為1ms,一個連接的QPS大約是1000
- 業(yè)務(wù)期望的QPS是50000
那么理論上需要的資源池大小是50000 / 1000 = 50個。但事實上這是個理論值,還要考慮到要比理論值預(yù)留一些資源,通常來講maxTotal可以比理論值大一些。
但這個值不是越大越好,一方面連接太多占用客戶端和服務(wù)端資源,另一方面對于Redis這種高QPS的服務(wù)器,一個大命令的阻塞即使設(shè)置再大資源池仍然會無濟于事。
2. maxIdle minIdle
maxIdle實際上才是業(yè)務(wù)需要的最大連接數(shù),maxTotal是為了給出余量,所以maxIdle不要設(shè)置過小,否則會有new Jedis(新連接)開銷,而minIdle是為了控制空閑資源監(jiān)測。
連接池的最佳性能是maxTotal = maxIdle ,這樣就避免連接池伸縮帶來的性能干擾。但是如果并發(fā)量不大或者maxTotal設(shè)置過高,會導(dǎo)致不必要的連接資源浪費。
可以根據(jù)實際總OPS和調(diào)用redis客戶端的規(guī)模整體評估每個節(jié)點所使用的連接池。
3.監(jiān)控
實際上最靠譜的值是通過監(jiān)控來得到“最佳值”的,可以考慮通過一些手段(例如jmx)實現(xiàn)監(jiān)控,找到合理值。
四、常見問題
1.資源“不足"
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool … Caused by: java.util.NoSuchElementException: Timeout waiting for idle object at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)
或者
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool … Caused by: java.util.NoSuchElementException: Pool exhausted at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:464)
兩種情況均屬于無法從資源池獲取到資源,但第一種是超時,第二種是因為blockWhenExhausted為false根本就不等。
遇到此類異常,不要盲目的認(rèn)為資源池不夠大,第三節(jié)已經(jīng)進行了分析。具體原因可以排查:網(wǎng)絡(luò)、資源池參數(shù)設(shè)置、資源池監(jiān)控(如果對jmx監(jiān)控)、代碼(例如沒執(zhí)行jedis.close())、慢查詢、DNS等問題。
具體可以參考該文章:https://www.atatech.org/articles/77799
2. 預(yù)熱JedisPool
由于一些原因(例如超時時間設(shè)置較小原因),有的項目在啟動成功后會出現(xiàn)超時。JedisPool定義最大資源數(shù)、最小空閑資源數(shù)時,不會真的把Jedis連接放到池子里,第一次使用時,池子沒有資源使用,會new Jedis,使用后放到池子里,可能會有一定的時間開銷,所以也可以考慮在JedisPool定義后,為JedisPool提前進行預(yù)熱,例如以最小空閑數(shù)量為預(yù)熱數(shù)量
List<Jedis> minIdleJedisList = new ArrayList<Jedis>(jedisPoolConfig.getMinIdle()); for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) { Jedis jedis = null; try { jedis = pool.getResource(); minIdleJedisList.add(jedis); jedis.ping(); } catch (Exception e) { logger.error(e.getMessage(), e); } finally { } } for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) { Jedis jedis = null; try { jedis = minIdleJedisList.get(i); jedis.close(); } catch (Exception e) { logger.error(e.getMessage(), e); } finally { } }
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java使用單向鏈表解決數(shù)據(jù)存儲自定義排序問題
本文主要介紹了java使用單向鏈表解決數(shù)據(jù)存儲自定義排序問題,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03詳解在Spring Boot框架下使用WebSocket實現(xiàn)消息推送
這篇文章主要介紹了詳解在Spring Boot框架下使用WebSocket實現(xiàn)消息推送,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2016-12-12java求100之內(nèi)的素數(shù)(質(zhì)數(shù))簡單示例
這篇文章主要介紹了java求100之內(nèi)的素數(shù)簡單示例,素數(shù)是一個大于1的自然數(shù),如果除了1和它自身外,不能被其他自然數(shù)整除的數(shù);否則稱為合數(shù)2014-04-04Java中注解@JsonFormat與@DateTimeFormat的使用
從數(shù)據(jù)庫獲取時間傳到前端進行展示的時候,我們有時候可能無法得到一個滿意的時間格式的時間日期,本文主要介紹了Java中注解@JsonFormat與@DateTimeFormat的使用,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08SpringBoot中引入MyBatisPlus的常規(guī)操作
這篇文章主要介紹了SpringBoot中引入MyBatisPlus的常規(guī)操作,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11SpringCloud?Tencent?全套解決方案源碼分析
Spring Cloud Tencent實現(xiàn)Spring Cloud標(biāo)準(zhǔn)微服務(wù)SPI,開發(fā)者可以基于Spring Cloud Tencent開發(fā)Spring Cloud微服務(wù)架構(gòu)應(yīng)用,Spring Cloud Tencent 的核心依托騰訊開源的一站式服務(wù)發(fā)現(xiàn)與治理平臺 Polarismesh,實現(xiàn)各種分布式微服務(wù)場景,感興趣的朋友一起看看吧2022-07-07Java中ArrayIndexOutOfBoundsException 異常報錯的解決方案
本文主要介紹了Java中ArrayIndexOutOfBoundsException 異常報錯的解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06