Giải thích hiện tượng Thundering Herd Problem và giải pháp Cache Stampede với Mutex
Khoảnh khắc chiếc chìa khóa Cache bị hết hạn Cache Expiration Hãy tưởng tượng trang chủ của bạn có một danh sách tin tức hot được lưu trong Redis với thời gian sống TTL là 10 phút. Trang chủ đang phụ…
☰ Mục lục bài viết (5 phần) Mở / Đóng mục lục
Khoảnh khắc chiếc chìa khóa Cache bị hết hạn (Cache Expiration)#
Hãy tưởng tượng trang chủ của bạn có một danh sách tin tức hot được lưu trong Redis với thời gian sống (TTL) là 10 phút. Trang chủ đang phục vụ 5.000 requests mỗi giây.
Vào đúng giây thứ 600, key trong Redis hết hạn và bị xóa. Trong đúng một giây đó:
- 5.000 requests cùng ập tới và cùng thấy cache bị miss!
- 5.000 requests cùng lúc lao thẳng xuống Database chính để chạy cùng một câu truy vấn SELECT nặng nề.
- Database lập tức sập nguồn do quá tải CPU và nghẽn connection pool.
Hiện tượng này được gọi là Cache Stampede hoặc Thundering Herd Problem (Đàn bò giẫm đạp).
Giải pháp 1: Khóa phân tán (Distributed Mutex)#
Chỉ cho phép duy nhất một request đầu tiên được phép truy vấn Database để tái tạo cache, các request còn lại bắt buộc phải chờ hoặc đọc tạm cache cũ:
async function getNewsWithLock(key: string) {
let data = await redis.get(key);
if (data) return JSON.parse(data);
// Thử lấy khóa với cờ NX (Not Exists)
const lockKey = `lock:${key}`;
const hasLock = await redis.set(lockKey, '1', 'EX', 5, 'NX');
if (hasLock) {
try {
// Chỉ duy nhất tiến trình này được query database!
const freshData = await db.query('SELECT * FROM hot_news');
await redis.set(key, JSON.stringify(freshData), 'EX', 600);
return freshData;
} finally {
await redis.del(lockKey);
}
} else {
// Các request khác chờ 50ms rồi thử đọc lại từ Redis
await sleep(50);
return getNewsWithLock(key);
}
}
Giải pháp 2: Thư viện Singleflight (Kỳ diệu trong Golang)#
Thư viện golang.org/x/sync/singleflight triệt tiêu hoàn toàn bài toán này: Nếu có 1.000 goroutine cùng gọi hàm lấy dữ liệu của cùng 1 key, singleflight chỉ thực thi hàm đó đúng 1 lần duy nhất và chia sẻ chung kết quả đó cho cả 1.000 caller!
Giải pháp 3: Tự động làm mới trước khi hết hạn (Probabilistic Early Expiration - XFetch)#
Sử dụng thuật toán xác suất để một vài request chủ động tính toán lại cache từ trước khi TTL chạm mốc 0.
Góc thảo luận#
Hệ thống caching của bạn đã có cơ chế phòng chống Cache Stampede chưa? Bạn chuộng dùng giải pháp Mutex hay Singleflight?
Xem thêm nội dung liên quan:
Thảo luận
1 phản hồi
Tham gia thảo luận
Bài liên quan trong #Backend & Microservices
-
Áp dụng Model Context Protocol (MCP) của Anthropic để kết nối AI với Postgres và Docker
Hoàng Nam (Tech Lead) · 💬 1 -
So sánh mô hình Embedding đa ngữ: OpenAI text-embedding-3 vs BGE-M3 vs Cohere
Minh Trí (AI Engineer) · 💬 1 -
Xây dựng Voice AI Agent thời gian thực với WebRTC và Whisper Live: Độ trễ dưới 500ms
Lan Anh (Senior Frontend) · 💬 1 -
Cấu hình Rate Limiting ở tầng Nginx và API Gateway: Chống DDOS Layer 7 hiệu quả
Đức Huy (DevOps Specialist) · 💬 1 -
Tokenization tiếng Việt của các LLM: Tại sao chi phí tiếng Việt đắt gấp 3 lần tiếng Anh?
Minh Trí (AI Engineer) · 💬 1