Lỗi xảy ra
Ứng dụng của bạn cố kết nối tới Redis và nhận được phản hồi này:
(error) ERR max number of clients reached
Redis đã đạt giới hạn client được cấu hình và đang từ chối các kết nối mới. Không có kết nối nào vào được cho đến khi có kết nối khác ngắt.
Có bốn nguyên nhân phổ biến gây ra lỗi này:
- Rò rỉ kết nối — ứng dụng của bạn mở kết nối nhưng không bao giờ đóng lại
- Nhiều instance ứng dụng mỗi cái duy trì kết nối riêng thay vì chia sẻ một pool chung
- Giới hạn
maxclientsmặc định (10.000) thực sự quá thấp cho lưu lượng của bạn - Một đợt tăng đột biến lưu lượng đã mở hàng nghìn kết nối ngắn hạn chưa được dọn dẹp
Bước 1: Kiểm tra số lượng client hiện tại
Đầu tiên, xác nhận vấn đề và xem bạn đang đối mặt với điều gì:
redis-cli INFO clients
Kết quả mẫu:
# 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
connected_clients ở mức 10.001 so với maxclients là 10.000 — đó là xác nhận của bạn.
Bây giờ xem những client đó đang thực sự làm gì:
redis-cli CLIENT LIST
Mỗi dòng hiển thị IP của client, lệnh cuối cùng và thời gian idle tính bằng giây. Các kết nối đang idle hơn 300 giây mà không có lệnh gần đây gần như chắc chắn là rò rỉ.
Bước 2: Kill các kết nối idle (Sửa nhanh)
Cần giải phóng slot ngay bây giờ mà không khởi động lại Redis? Hãy kill các kết nối idle.
Trên Redis 7.4+, một lệnh là đủ:
# Kill tất cả client idle hơn 5 phút
redis-cli CLIENT KILL MAXAGE 300
Trên các phiên bản cũ hơn, phân tích CLIENT LIST thủ công:
redis-cli CLIENT LIST | awk -F'[= ]' '{
for(i=1;i= 300) print id
}
}' | xargs -I {} redis-cli CLIENT KILL ID {}
Hoặc nhắm vào một kết nối cụ thể theo ID từ kết quả CLIENT LIST:
redis-cli CLIENT KILL ID 12345
Điều này giúp bạn có thêm thời gian xử lý. Nó sẽ không ngăn vấn đề tái diễn.
Bước 3: Tăng maxclients (Nếu giới hạn quá thấp)
Đôi khi giới hạn không đủ cao cho khối lượng công việc thực tế của bạn. Đây là cách tăng nó.
Tùy chọn A — Thay đổi khi đang chạy (không cần khởi động lại)
redis-cli CONFIG SET maxclients 20000
Tùy chọn B — Thay đổi trong redis.conf (giữ nguyên sau khi khởi động lại)
Mở file cấu hình Redis — thường tại /etc/redis/redis.conf hoặc /etc/redis.conf:
sudo nano /etc/redis/redis.conf
Tìm và cập nhật:
maxclients 20000
Sau đó tải lại:
redis-cli CONFIG REWRITE
sudo systemctl reload redis
Tùy chọn C — Docker / docker-compose
services:
redis:
image: redis:7
command: redis-server --maxclients 20000
Giới hạn file descriptor của OS: Redis giới hạn maxclients ở mức giới hạn file descriptor của OS trừ 32 (dành cho sử dụng nội bộ). Nếu bạn đặt 20.000 nhưng ulimit -n trả về 1024, Redis sẽ âm thầm áp dụng giá trị thấp hơn nhiều. Sửa trong /etc/security/limits.conf:
redis soft nofile 65535
redis hard nofile 65535
Bước 4: Sửa rò rỉ kết nối trong ứng dụng
Tăng giới hạn chỉ là giải pháp tạm thời. Nếu kết nối đang rò rỉ, bạn sẽ lại đạt đến giới hạn — chỉ là muộn hơn thôi. Tìm ra nơi ứng dụng của bạn không giải phóng kết nối và sửa nó ở đó.
Node.js (ioredis)
Lỗi phổ biến nhất: tạo một Redis client mới cho mỗi request. Mỗi lần gọi mở một kết nối mới không bao giờ đóng.
// XẤU — kết nối mới cho mỗi request, không cái nào được đóng
app.get('/data', async (req, res) => {
const client = new Redis();
const val = await client.get('key');
res.json(val);
});
// TỐT — một client được tạo một lần, dùng chung cho tất cả request
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)
Dùng ConnectionPool với giới hạn tường minh để pool không thể tăng không giới hạn:
import redis
pool = redis.ConnectionPool(
host='localhost',
port=6379,
max_connections=50
)
client = redis.Redis(connection_pool=pool)
PHP (Predis / PhpRedis)
PHP-FPM cấp cho mỗi worker một kết nối persistent riêng. Hãy tính toán trước khi tăng maxclients: pm.max_children × number_of_servers. Ví dụ, 50 worker trên 4 app server có nghĩa là tối thiểu 200 kết nối — đặt maxclients cao hơn mức đó với dư địa an toàn.
Bước 5: Đặt timeout cho kết nối
Để Redis tự động dọn dẹp các kết nối bị bỏ rơi. Thêm hai dòng này vào redis.conf:
# Ngắt các client im lặng trong 5 phút
timeout 300
# Gửi probe TCP keepalive mỗi 60 giây
tcp-keepalive 60
Áp dụng mà không cần khởi động lại:
redis-cli CONFIG SET timeout 300
redis-cli CONFIG SET tcp-keepalive 60
Xác minh bản sửa lỗi
Theo dõi số lượng client trực tiếp trong khi ứng dụng chạy với tải thông thường:
watch -n 2 'redis-cli INFO clients | grep connected_clients'
connected_clients nên duy trì ở mức thoải mái dưới maxclients. Nếu trước đây nó liên tục tăng, bạn sẽ thấy nó ổn định hơn bây giờ.
Xác nhận giới hạn mới đã có hiệu lực:
redis-cli CONFIG GET maxclients
Tóm tắt nhanh
- Giải quyết ngay lập tức: Kill các kết nối idle với
CLIENT KILL MAXAGE 300(Redis 7.4+) hoặcCLIENT KILL ID - Sửa tạm thời: Tăng
maxclientsbằngCONFIG SET - Sửa vĩnh viễn: Đặt
maxclientstrongredis.conf, sửa rò rỉ kết nối trong code ứng dụng, tăng giới hạn file descriptor của OS - Phòng ngừa: Dùng connection pool, đặt
timeouttrong cấu hình Redis, theo dõiconnected_clientsliên tục — Prometheus + redis_exporter là cách thiết lập chuẩn cho việc này

