Redis expiration và eviction: key biến mất vì hết hạn hay vì thiếu memory? Cơ bản
Thời gian đọc ước tính: 8 phút
Vấn đề
Phần tiêu đề “Vấn đề”API đọc đơn hàng từ PostgreSQL và lưu bản sao vào Redis 10 phút. Một buổi chiều database tăng tải vì cache miss nhiều hơn, và key order:42 biến mất trước hạn. Kết luận vội “TTL của Redis sai” thường sai: key có thể mất theo hai đường độc lập.
| Expiration | Eviction | |
|---|---|---|
| Nguyên nhân | Thời hạn của key đã qua | Redis chạm maxmemory và cần nhường chỗ |
| Có thể xảy ra trước TTL? | Không | Có |
| Metric | expired_keys |
evicted_keys |
| Xử lý | Chỉnh TTL, jitter | Thêm RAM, giảm big key, đổi policy |
Tăng TTL khi Redis đang evict vì thiếu memory chỉ làm tình hình xấu hơn.
Default thật của Redis
Phần tiêu đề “Default thật của Redis”| Cấu hình | Default | Hệ quả |
|---|---|---|
maxmemory |
0 (không giới hạn, trên hệ 64-bit) |
Không bao giờ evict; Redis lớn tới khi OOM của hệ điều hành |
maxmemory-policy |
noeviction |
Khi đầy: lệnh ghi bị từ chối, lệnh đọc vẫn chạy |
maxmemory-samples |
5 |
LRU/LFU chỉ xấp xỉ |
Vì thế một Redis dùng làm cache mà không đặt maxmemory sẽ không bao giờ evict, và đặt maxmemory mà không đổi policy sẽ trả lỗi ghi khi đầy. Cache thuần cần đặt cả hai.
Khái niệm cốt lõi
Phần tiêu đề “Khái niệm cốt lõi”TTL là thời gian còn lại của key. SET key v EX 600 ghi giá trị cùng hạn; TTL trả -1 nếu key tồn tại nhưng không có hạn, -2 nếu key không tồn tại. SET ghi đè xoá TTL của key (dùng KEEPTTL để giữ), còn INCR, LPUSH, HSET giữ nguyên TTL.
Cách Redis xoá key hết hạn
Phần tiêu đề “Cách Redis xoá key hết hạn”- Lazy (thụ động): key bị xoá khi một lệnh truy cập nó và thấy đã quá hạn.
- Active (chủ động): theo tài liệu latency của Redis, chu kỳ chạy 10 lần mỗi giây; mỗi chu kỳ lấy mẫu 20 key có TTL, xoá các key đã hết hạn, và lặp lại nếu hơn 25% mẫu đã hết hạn.
Hệ quả: key đã hết hạn có thể vẫn chiếm RAM một lúc, và nếu rất nhiều key hết hạn cùng giây (chiếm từ 25% số key có TTL), Redis có thể bị chặn để dọn. Đây là lý do cần jitter TTL.
Replica không tự hết hạn
Phần tiêu đề “Replica không tự hết hạn”Master xoá key rồi gửi DEL cho replica; replica không tự expire độc lập (nhưng giữ đủ thông tin hạn để tiếp quản khi lên làm master). Hệ quả:
- Trong lúc chờ
DELđến, key đã quá hạn vẫn chiếm memory trên replica. - Theo tài liệu replication của Redis, replica dùng đồng hồ logic để báo key không tồn tại với lệnh đọc, nên client đọc từ replica thường không thấy giá trị đã hết hạn.
Ngược lại, theo redis.conf, từ Redis 5 replica mặc định bỏ qua maxmemory của chính nó (replica-ignore-maxmemory yes); eviction diễn ra ở master.
Các policy
Phần tiêu đề “Các policy”| Policy | Ứng viên | Ghi chú |
|---|---|---|
noeviction (default) |
Không có | Ghi bị từ chối khi đầy |
allkeys-lru |
Mọi key | LRU xấp xỉ; Redis khuyên làm lựa chọn mặc định khi không có lý do khác |
allkeys-lfu |
Mọi key | Giữ key truy cập thường xuyên; tinh chỉnh bằng lfu-log-factor (10), lfu-decay-time (1 phút) |
allkeys-random |
Mọi key | Hợp khi các key được truy cập đều nhau |
volatile-lru/lfu/random |
Chỉ key có TTL | Hợp khi một instance vừa cache vừa lưu key bền |
volatile-ttl |
Key có TTL | Evict key sắp hết hạn trước |
Mọi policy volatile-* hành xử như noeviction khi không còn key nào có TTL. maxmemory-samples (mặc định 5) là số key lấy mẫu mỗi lần evict; tăng lên 10 xấp xỉ LRU thật hơn với chút CPU thêm.
Chọn policy theo workload
Phần tiêu đề “Chọn policy theo workload”| Workload | Policy | Lý do |
|---|---|---|
| Cache thuần, vài key nóng | allkeys-lru (hoặc allkeys-lfu) |
Tận dụng phân bố truy cập lệch |
| Cache thuần, truy cập đều | allkeys-random |
Không có key “nóng” để ưu tiên |
| Cache lẫn session/key bền trong một instance | Tách instance; nếu bắt buộc, volatile-* |
allkeys-* có thể evict cả session |
| Queue, lock, dữ liệu không được mất | noeviction |
Thà lỗi ghi còn hơn mất dữ liệu lặng lẽ |
Cách hoạt động
Phần tiêu đề “Cách hoạt động”SET EXỨng dụng ghi cache key cùng TTL. Redis lưu giá trị và thời điểm hết hạn.
Timeline một key SET EX 2:
| Mốc | Quan sát | Ghi chú |
|---|---|---|
| T0 | Key có giá trị, TTL khoảng 2 |
|
| T0 + 1 s | Còn, hoặc đã bị evict nếu áp lực memory | Eviction nhìn từ ứng dụng giống expiration |
| T0 + 2,3 s | GET trả nil, TTL = -2 |
Byte được giải phóng sớm hay muộn là chuyện nội bộ |
TTL là giới hạn tối đa của độ cũ, không đảm bảo dữ liệu mới trong khoảng đó. Đơn hàng có thể đổi trong PostgreSQL ngay sau khi ghi cache; xem cache-aside.
Chẩn đoán: miss tăng thì xem gì
Phần tiêu đề “Chẩn đoán: miss tăng thì xem gì”Đang tải diagram…
Lệnh cần chạy:
redis-cli INFO stats | grep -E "expired_keys|evicted_keys|keyspace_hits|keyspace_misses"redis-cli INFO memory | grep -E "used_memory_human|maxmemory_human|mem_not_counted_for_evict"redis-cli CONFIG GET maxmemory-policyHit rate = keyspace_hits / (keyspace_hits + keyspace_misses). Đừng cộng expired_keys + evicted_keys rồi coi là số miss: một key hết hạn mà không ai đọc không gây miss, còn một hot key bị evict gây hàng nghìn miss.
Ví dụ thực tế
Phần tiêu đề “Ví dụ thực tế”Demo local
Phần tiêu đề “Demo local”docker compose -f examples/docker-compose.yml up -d redisnpx --prefix examples ts-node examples/src/redis-expiration/ttl-demo.tsdocker compose -f examples/docker-compose.yml stop redisScript làm ba bước:
- Ghi một key sống 2 giây, in giá trị và TTL (
before: cached-order, TTL gần 2). - Đợi 2,3 giây rồi đọc lại (
after: null TTL: -2). - In
maxmemory-policycủa container, thường lànoevictiontheo default ở trên.
Demo chỉ chứng minh expiration; nó không đẩy Redis tới maxmemory. Để thấy eviction, dùng một Redis test riêng với maxmemory nhỏ và đọc INFO stats; đừng CONFIG SET maxmemory trên instance đang phục vụ dữ liệu thật.
Trade-offs
Phần tiêu đề “Trade-offs”TTL và policy đem lại gì
- TTL đặt giới hạn tồn tại cho cache entry và tự dọn dữ liệu không ai đọc
- Eviction cho Redis tự kiểm soát memory khi cache tăng ngoài dự tính
- Nhiều policy cho phép ưu tiên hot key hoặc key sắp hết hạn
Giới hạn cần dự liệu
- TTL dài tăng nguy cơ stale; TTL ngắn tăng miss và tải DB
- Eviction có thể xoá key còn TTL, kéo hit rate xuống
- Policy sai với workload hỗn hợp có thể evict dữ liệu quan trọng hoặc làm ghi thất bại
Checklist production
Phần tiêu đề “Checklist production”- Đặt
maxmemoryrõ ràng và chừa RAM cho replication/AOF buffer (xemmem_not_counted_for_evicttrongINFO memory); các buffer này không tính vàomaxmemory. - Đặt
maxmemory-policyđúng workload; không đểnoevictionmặc định cho cache thuần. - Không trộn cache với dữ liệu không được mất trong một instance; tách instance.
- TTL có jitter cho key nạp hàng loạt.
- Cảnh báo theo tốc độ tăng của
evicted_keysvà hit rate, không chỉused_memory. - Replica: key hết hạn vẫn chiếm memory tới khi
DELtừ master đến (lệnh đọc trên replica đã báo key không tồn tại); theo dõi memory của replica. - Redis không phải nơi lưu duy nhất của dữ liệu nghiệp vụ nếu có eviction.
Sai lầm thường gặp
Phần tiêu đề “Sai lầm thường gặp”Tóm tắt
Phần tiêu đề “Tóm tắt”Tự kiểm tra: (1) evicted_keys tăng trong khi nhiều key còn TTL: tăng TTL có giúp không? (2) Vì sao volatile-ttl có thể làm ghi lỗi dù còn key không TTL? (3) Vì sao SET ghi đè là bẫy với TTL?