JedisPool は Jedis クライアント用のコネクションプールです。プールパラメーターを適切に設定すると、Redis のパフォーマンスとリソース使用率が向上します。このトピックでは、JedisPool の使用方法、パラメーターの説明、および最適化に関する推奨事項について説明します。
使用方法
この例では Jedis 2.9.0 を使用します。Maven の依存関係は次のとおりです。
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
<scope>compile</scope>
</dependency>
Jedis は Apache Commons-pool2 を使用してプールを管理します。主要なパラメータークラスは GenericObjectPoolConfig です。
GenericObjectPoolConfig jedisPoolConfig = new GenericObjectPoolConfig();
jedisPoolConfig.setMaxTotal(...);
jedisPoolConfig.setMaxIdle(...);
jedisPoolConfig.setMinIdle(...);
jedisPoolConfig.setMaxWaitMillis(...);
...
JedisPool を初期化します。
// redisHost はインスタンスの IP アドレスです。 redisPort はインスタンスのポートです。 redisPassword はインスタンスのパスワードです。 timeout は接続タイムアウトと読み取り/書き込みタイムアウトの両方です。
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 {
// JedisPool モードでは、Jedis オブジェクトはリソースプールに返されます。
if (jedis != null)
jedis.close();
}
パラメーター
JedisPool は、スレッドセーフな有界プールで Jedis 接続を管理します。GenericObjectPoolConfig を適切に設定することで、Redis のパフォーマンスが向上し、リソースオーバーヘッドが削減されます。次の表に、主要なパラメーターと推奨設定を示します。
表 1: リソース設定と使用方法に関連するパラメーター
|
パラメーター |
説明 |
デフォルト値 |
推奨事項 |
|
maxTotal |
プール内の最大接続数。 |
8 |
|
|
maxIdle |
プール内の最大アイドル接続数。 |
8 |
|
|
minIdle |
プールで維持される最小アイドル接続数。 |
0 |
|
|
blockWhenExhausted |
プールが枯渇した場合に呼び出し元がブロックされるかどうか。maxWaitMillis は、これが true の場合にのみ有効になります。 |
true |
デフォルト値を使用します。 |
|
maxWaitMillis |
プールが枯渇した場合の最大待機時間 (ms)。 |
-1 (タイムアウトなし) |
デフォルト値は使用しないでください。 |
|
testOnBorrow |
プールから接続を取得する際に接続を検証 (ping) するかどうか。無効な接続は削除されます。 |
false |
トラフィックが多い場合は、ping のオーバーヘッドを削減するために false に設定します。 |
|
testOnReturn |
プールに接続を返す際に接続を検証 (ping) するかどうか。無効な接続は削除されます。 |
false |
トラフィックが多い場合は、ping のオーバーヘッドを削減するために false に設定します。 |
|
jmxEnabled |
JMX モニタリングを有効にするかどうか。 |
true |
これを有効にします。アプリケーションでも有効になっていることを確認してください。 |
以下の 4 つのパラメーターは、アイドル接続の検出を制御します。
表 2: アイドルリソース検出に関連するパラメーター
|
名前 |
説明 |
デフォルト値 |
推奨事項 |
|
testWhileIdle |
エビクションの実行中にアイドル接続を検証 (ping) するかどうか。無効な接続は破棄されます。 |
false |
true |
|
timeBetweenEvictionRunsMillis |
アイドル接続のエビクション実行間の間隔 (ms)。 |
-1 (検出なし) |
検出を有効にするにはこれを設定します。期間を選択するか、以下の JedisPoolConfig のデフォルト値を使用できます。 |
|
minEvictableIdleTimeMillis |
接続がエビクションの対象となるまでの最小アイドル時間 (ms)。 |
1,800,000 (30 分) |
デフォルト (30 分) で通常は十分です。以下の JedisPoolConfig のデフォルト値も使用できます。 |
|
numTestsPerEvictionRun |
エビクション実行ごとにテストされる接続数。 |
3 |
プールサイズに基づいて調整します。エビクション実行ごとにすべての接続をチェックするには、-1 に設定します。 |
JedisPoolConfig は GenericObjectPoolConfig を拡張し、以下のアイドル検出のデフォルト値を提供します。
public class JedisPoolConfig extends GenericObjectPoolConfig {
public JedisPoolConfig() {
setTestWhileIdle(true);
setMinEvictableIdleTimeMillis(60000);
setTimeBetweenEvictionRunsMillis(30000);
setNumTestsPerEvictionRun(-1);
}
}
すべてのデフォルト値は org.apache.commons.pool2.impl.BaseObjectPoolConfig クラスで定義されています。
主要パラメーターの推奨設定
maxTotal (最大接続数)
maxTotal を設定する際には、次の要因を考慮してください。
-
想定される Redis の同時実行数。
-
クライアント側のコマンド実行時間。
-
利用可能な Redis リソース。ノード数 (アプリケーションの Elastic Compute Service (ECS) インスタンス) に
maxTotalを乗じた値が、インスタンスに許可されている最大接続数を超えてはなりません。この制限については、インスタンスの詳細ページで確認してください。 -
アイドル接続や頻繁なプールスケーリングによるリソースオーバーヘッド。
例:平均コマンド時間 (借用 + 実行 + ネットワーク) が 1 ms の場合、1 つの接続で約 1,000 QPS を処理できます。単一インスタンスで 50,000 QPS を目標とする場合 (合計 QPS / シャード数)、理論上の maxTotal は 50,000 / 1,000 = 50 です。
理論値にバッファを追加します。ただし、maxTotal を過剰に設定すると、クライアントとサーバーのリソースを浪費し、大規模なコマンドがブロッキングを引き起こす場合には役立ちません。
maxIdle と minIdle
maxIdle はワークロードが必要とする実際のピーク接続数を表し、maxTotal はバッファを提供します。maxIdle を低く設定しすぎると、プールが頻繁に new Jedis 接続を作成することになるため避けてください。minIdle はアイドルリソースの検出を制御します。
maxTotal が maxIdle と等しい場合に最高のパフォーマンスが得られ、プールのスケーリングによるオーバーヘッドを回避できます。ワークロードにトラフィックのスパイクがある場合は、これらを等しく設定します。同時実行数が低い場合や maxIdle が高すぎる場合は、アイドル接続が無駄になります。
合計 QPS とクライアントインスタンスの数に基づいて、ノードあたりのプールサイズを見積もります。
モニタリングを使用して妥当な値を取得する
本番環境では、JMX やその他のモニタリングを使用して、実際の使用パターンに基づいて最適な値を決定します。
よくある質問
リソース不足
プールからリソースを取得できなくなるケースは、以下のとおりです。
-
タイムアウト:
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) -
blockWhenExhausted パラメーターが false に設定されているため、呼び出しはリソースが解放されるのを待機しません。
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)
この例外は、必ずしもプールサイズの不足が原因で発生するわけではありません。 ネットワークの問題、設定が誤っているプールパラメーター、JMX モニタリング、jedis.close() 呼び出しの欠落、低速なクエリ、または DNS の問題が原因である可能性もあります。 主要パラメーターの推奨設定。
JedisPool のウォームアップ
JedisPool は初期化時に接続を積極的に作成しません。最初のリクエストが new Jedis 接続をトリガーし、これによりレイテンシーが発生します。タイムアウトが短い場合、起動時にタイムアウトが発生する可能性があります。これを回避するには、プール作成後に minIdle に基づいて接続を事前にフェッチします。
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 {
}
}
その他のエラーについては、「Common errors」をご参照ください。