エラーの内容
アプリがRedisに接続しようとすると、以下のエラーが返されます:
(error) ERR max number of clients reached
Redisが設定されたクライアント数の上限に達し、新しい接続を拒否しています。他の接続が切断されるまで、新しい接続は受け付けられません。
主な原因は4つあります:
- 接続リーク — アプリが接続を開いたまま閉じない
- 複数のアプリインスタンスがプールを共有せず、それぞれ独自の接続を維持している
- デフォルトの
maxclients制限(10,000)が実際のトラフィックに対して低すぎる - トラフィックの急増により大量の短命な接続が開かれ、まだクリーンアップされていない
ステップ 1: 現在のクライアント数を確認する
まず問題を確認し、状況を把握します:
redis-cli INFO clients
出力例:
# Clients
connected_clients:10001
cluster_connections:0
maxclients:10000
client_recent_max_input_buffer:20480
client_recent_max_output_buffer:0
blocked_clients:0
tracking_clients:0
clients_in_timeout_table:0
maxclients が10,000に対して connected_clients が10,001 — これが問題の確認です。
次に、それらのクライアントが実際に何をしているか確認します:
redis-cli CLIENT LIST
各行にはクライアントのIPアドレス、最後のコマンド、アイドル時間(秒)が表示されます。最近のコマンドがなく300秒以上アイドル状態の接続は、ほぼ確実にリークです。
ステップ 2: アイドル接続を切断する(応急処置)
Redisを再起動せずに今すぐスロットを空けたい場合は、アイドル接続を切断します。
Redis 7.4以降では、1つのコマンドで対応できます:
# 5分以上アイドル状態のクライアントをすべて切断
redis-cli CLIENT KILL MAXAGE 300
古いバージョンでは、CLIENT LIST を手動で解析します:
redis-cli CLIENT LIST | awk -F'[= ]' '{
for(i=1;i= 300) print id
}
}' | xargs -I {} redis-cli CLIENT KILL ID {}
または、CLIENT LIST の出力からIDを指定して特定の接続を切断します:
redis-cli CLIENT KILL ID 12345
これで一時的な余裕ができますが、問題が再発するのを防ぐことはできません。
ステップ 3: maxclients を増やす(制限が低すぎる場合)
実際のワークロードに対して制限が不十分な場合があります。以下の方法で上限を引き上げます。
オプション A — ランタイムで変更する(再起動不要)
redis-cli CONFIG SET maxclients 20000
オプション B — redis.conf で変更する(再起動後も有効)
通常 /etc/redis/redis.conf または /etc/redis.conf にあるRedisの設定ファイルを開きます:
sudo nano /etc/redis/redis.conf
以下の行を見つけて更新します:
maxclients 20000
その後、再読み込みします:
redis-cli CONFIG REWRITE
sudo systemctl reload redis
オプション C — Docker / docker-compose
services:
redis:
image: redis:7
command: redis-server --maxclients 20000
OSのファイルディスクリプタ制限: RedisはOSのファイルディスクリプタ制限から32(内部使用のために予約)を引いた値を maxclients の上限とします。20,000に設定しても ulimit -n が 1024 を返す場合、Redisは警告なしにはるかに低い値を適用します。/etc/security/limits.conf で修正してください:
redis soft nofile 65535
redis hard nofile 65535
ステップ 4: アプリの接続リークを修正する
制限を引き上げるのは応急処置に過ぎません。接続がリークしている場合、いずれまた上限に達します。アプリのどこで接続が解放されていないかを特定して修正してください。
Node.js (ioredis)
最も多い間違い:リクエストごとに新しいRedisクライアントを作成すること。呼び出しのたびに新しい接続が開かれ、一度も閉じられません。
// BAD — リクエストごとに新しい接続を作成し、一度も閉じない
app.get('/data', async (req, res) => {
const client = new Redis();
const val = await client.get('key');
res.json(val);
});
// GOOD — クライアントを一度作成し、すべてのリクエストで共有する
const redis = new Redis({ host: 'localhost', port: 6379 });
app.get('/data', async (req, res) => {
const val = await redis.get('key');
res.json(val);
});
Python (redis-py)
プールが無制限に増加しないよう、明示的な上限を設定した ConnectionPool を使用します:
import redis
pool = redis.ConnectionPool(
host='localhost',
port=6379,
max_connections=50
)
client = redis.Redis(connection_pool=pool)
PHP (Predis / PhpRedis)
PHP-FPMは各ワーカーに独自の永続接続を持たせます。maxclients を上げる前に計算してください:pm.max_children × サーバー台数。例えば、4台のアプリサーバーに50ワーカーずつあれば、最低200接続が必要です — 余裕を持って maxclients をその値以上に設定してください。
ステップ 5: 接続タイムアウトを設定する
放棄された接続を自動的にクリーンアップするようRedisを設定します。redis.conf に以下の2行を追加します:
# 5分間無通信のクライアントを切断
timeout 300
# 60秒ごとにTCPキープアライブプローブを送信
tcp-keepalive 60
再起動なしで適用します:
redis-cli CONFIG SET timeout 300
redis-cli CONFIG SET tcp-keepalive 60
修正の確認
アプリが通常負荷で動作している間、クライアント数をリアルタイムで監視します:
watch -n 2 'redis-cli INFO clients | grep connected_clients'
connected_clients は maxclients を大幅に下回った状態で安定するはずです。以前は増加し続けていたなら、今は安定するのが確認できるはずです。
新しい制限が有効になったことを確認します:
redis-cli CONFIG GET maxclients
クイックリファレンス
- 即時対応:
CLIENT KILL MAXAGE 300(Redis 7.4以降)またはCLIENT KILL IDでアイドル接続を切断 - 短期的な対処:
CONFIG SETでmaxclientsを引き上げる - 恒久的な修正:
redis.confでmaxclientsを設定し、アプリコードの接続リークを修正し、OSのファイルディスクリプタ制限を引き上げる - 予防策:接続プールを使用し、Redisの設定で
timeoutを設定し、connected_clientsを継続的に監視する — Prometheus + redis_exporterがこの用途の標準的なセットアップです

