Giao diện
Cache: nền tảng
Cache là bản sao tạm của dữ liệu đặt ở nơi đọc nhanh hơn nguồn gốc, thường là RAM, để không phải hỏi database mỗi lần. Hình dung database là thư viện ở ngoại thành, mỗi lần đọc mất một tiếng đi xe; cache là cái bàn làm việc trước mặt, để sẵn những cuốn hay đọc nhất.
Thêm cache không làm hệ thống đơn giản hơn. Nó chuyển độ phức tạp sang chỗ khác: dữ liệu có thể lệch, thêm một thành phần có thể hỏng, và các lỗi khó debug nhất.
Vì sao một cache nhỏ gánh được phần lớn tải
Đưa cả database vào RAM thường quá đắt. Cache vẫn hiệu quả nhờ cách người dùng truy cập dữ liệu:
- Truy cập lệch: một phần nhỏ dữ liệu nhận phần lớn lượt đọc. Nguyên lý Pareto mô tả kiểu “80% traffic vào 20% dữ liệu”; phân phối Zipf mô tả chính xác hơn: phần tử xếp hạng k được truy cập tỷ lệ với 1/k.
- Cục bộ theo thời gian: dữ liệu vừa được đọc có xu hướng sắp được đọc lại.
- Cục bộ theo không gian: dữ liệu nằm gần nhau hay được đọc cùng nhau (lý do database đọc cả page 16KB, MySQL chương 1).
Nhờ vậy, cache khoảng 20–30% dữ liệu nóng thường đủ gánh phần lớn lượt đọc.
Khi nào nên và không nên dùng cache
| Nên dùng | Không nên dùng |
|---|---|
| Đọc nhiều hơn ghi rất nhiều (thường từ 90:10) | Ghi nhiều: chi phí xoá cache liên tục ăn hết lợi ích |
| Kết quả tốn công tính (tổng hợp, báo cáo) | Không chấp nhận dữ liệu cũ dù chỉ vài mili giây (số dư, khớp lệnh) |
| Dữ liệu ít đổi (cấu hình, hồ sơ, file tĩnh) | Query đã nhanh và database chưa quá tải: thêm cache chỉ thêm độ phức tạp |
Trước khi thêm cache, xem bước tối ưu query và index đã hết dư địa chưa (Phân tích bài toán đọc nhiều).
Đặt cache ở đâu
Hệ thống lớn có cache ở nhiều tầng, càng gần người dùng càng nhanh:
- Client: trình duyệt, app mobile cache theo header HTTP (
Cache-Control: max-age,ETag+If-None-Matchtrả 304,stale-while-revalidatetheo RFC 5861). - Proxy: reverse proxy (Nginx, Varnish) và CDN đứng trước backend, trả lời thay cho server. CDN là mạng reverse proxy đặt ở nhiều điểm gần người dùng. Forward proxy thì ngược lại, đứng phía client (proxy công ty).
- Server-side: phần backend dev làm nhiều nhất, chia hai loại:
| Local cache (trong process) | Remote cache (Redis, Memcached) | |
|---|---|---|
| Tốc độ | Dưới micro giây, không qua mạng | Khoảng dưới 1–2 ms, qua mạng |
| Dùng chung | Mỗi instance một bản riêng | Mọi instance dùng chung |
| Hit rate | Thấp hơn (mỗi instance phải tự nạp) | Cao hơn |
| Nhất quán | Khó: xoá một key phải xoá trên mọi instance | Một chỗ để xoá |
| Ví dụ | Caffeine, lru-cache (Node) | Redis, Memcached |
Cache nhiều tầng (L1 + L2): local cache làm L1, Redis làm L2. Hay dùng cho key cực nóng. Khó nhất là xoá L1 trên mọi instance khi dữ liệu đổi: phát sự kiện qua Redis Pub/Sub, hoặc dùng client-side caching của Redis (CLIENT TRACKING, Redis tự báo client khi key đổi). L1 nên có TTL ngắn để sai lệch tự hết.
Ai đọc database: cache-aside hay inline
- Cache-aside (look-aside, lazy loading): app điều phối. Đọc cache; miss thì app đọc database rồi ghi vào cache. Phổ biến nhất, cache sập thì app vẫn đọc thẳng database được.
- Inline (read-through / write-through): app chỉ nói chuyện với cache, lớp cache tự đọc ghi database. CDN, Nginx
proxy_cache, CaffeineLoadingCachelà inline cache.
Chi tiết từng chiến lược đọc ghi: Chiến lược đọc ghi.
Hit rate, AMAT và p99
Hit rate = số lần tìm thấy trong cache / tổng số lần đọc. AMAT (average memory access time) đo độ trễ trung bình:
AMAT = Hit time + Miss rate × Miss penaltyVới hit time 1 ms (Redis) và miss penalty 100 ms (database): miss 5% cho AMAT = 1 + 5% × 100 = 6 ms; miss 1% cho 2 ms. Giảm miss rate 4 điểm phần trăm làm latency trung bình giảm 3 lần.
Trung bình đẹp chưa chắc p99 đẹp. p99 nhìn 1% request chậm nhất: khi miss rate vượt 1%, toàn bộ nhóm 1% chậm nhất là request phải xuống database. Hit rate 99,5% cho p99 khoảng 1 ms; 98,5% cho p99 khoảng 100 ms, tệ đi 100 lần. Request chậm còn dồn tải lên database, làm chính database chậm thêm.
Một trang thường đọc nhiều key song song; request chỉ xong khi key chậm nhất xong. Với 50 key mỗi request, hit rate 99,5% cho mỗi key nghĩa là khoảng 22% request có ít nhất một key miss.
Một trang thường đọc nhiều key song song. Request chỉ xong khi key chậm nhất xong.
1,5 msAMAT mỗi lần đọc
1 msp50 request
1 msp99 request
101 msp99,9 request
1.000 request, xếp từ nhanh đến chậm
chỉ đọc cachephải xuống database10 request chậm nhất = vùng p99
Miss rate 0,5% nhỏ hơn 1%, nên trong 10 request chậm nhất vẫn có request chỉ đọc cache: p99 = 1 ms. AMAT = 1 + 0,5% × 100 = 1,5 ms.
Muốn giữ p99 thấp, miss rate phải thấp hơn ngưỡng percentile đang theo dõi, và càng thấp khi mỗi request đọc càng nhiều key.
Ghi nhớ
- Cache hiệu quả vì truy cập lệch (Pareto, Zipf) và có tính cục bộ.
- Không dùng cache cho dữ liệu ghi nhiều, cần mới tuyệt đối, hoặc query vốn đã nhanh.
- Local cache nhanh nhưng khó xoá đồng loạt; remote cache dùng chung; L1 + L2 cần cơ chế xoá L1.
- AMAT = hit time + miss rate × miss penalty; miss rate vượt 1% là p99 bằng thời gian đọc database.