Persistence: RDB, AOF và eviction policy
Trả lời giúp mình câu này: Redis lưu dữ liệu trên RAM, vậy restart Redis thì có mất hết data không?
Không nhất thiết. Redis có cơ chế persistence ghi dữ liệu xuống disk; khi restart nó đọc lại để khôi phục. Nhưng tùy cấu hình, vẫn có thể mất một phần dữ liệu gần thời điểm crash.
1. Bài toán
Trong shop-app, Redis giữ hai loại dữ liệu:
product:100 → cache, mất thì load lại từ DB (mất được)
session:abc123 → session đăng nhập (mất = cả nghìn user bị logout)
rate:order:42:... → counter rate limit (mất được)
sale:12h:winner → ai thắng flash sale (mất = sai nghiệp vụ)
2. Vấn đề
Redis restart (deploy, OOM, máy chết) mà không có persistence thì RAM trống. Cache thì chịu được (chỉ miss một lượt, cẩn thận avalanche). Session và winner thì không.
3. Giải pháp: RDB và AOF
RDB (Redis Database): chụp snapshot toàn bộ dữ liệu tại một thời điểm ra file dump.rdb. Redis fork() một process con để ghi; process cha vẫn phục vụ (copy-on-write).
RAM ── fork + snapshot ──> dump.rdb (10:00)
10:01 SET session:x
10:02 INCR counter
10:03 crash → restart chỉ có dữ liệu lúc 10:00 (mất 3 phút)
AOF (Append Only File): ghi lại mọi write command vào log. Restart thì chạy lại log.
SET session:x ... ─┐
INCR counter ├─> appendonly.aof → restart: replay
DEL product:10 ─┘
Mức mất dữ liệu của AOF phụ thuộc appendfsync:
appendfsync | Khi nào fsync xuống disk | Có thể mất | Hiệu năng |
|---|---|---|---|
always | Mỗi write | Gần như không | Chậm nhất |
everysec (mặc định) | Mỗi giây | ~1 giây dữ liệu | Tốt, cân bằng |
no | Để OS quyết định (~30s) | Vài chục giây | Nhanh nhất |
AOF lớn dần, nên Redis có AOF rewrite (BGREWRITEAOF): viết lại thành tập command tối thiểu. Từ Redis 7, AOF là nhiều file (base + incremental) quản lý bằng manifest.
4. Code từng bước (cấu hình)
Bước 1: Redis chỉ làm cache (tắt persistence, nhanh nhất):
# redis-cache.conf
save ""
appendonly no
maxmemory 2gb
maxmemory-policy allkeys-lru
Bước 2: Redis giữ session/winner (bật cả hai):
# redis-store.conf
save 900 1 # 15 phút có ≥1 thay đổi thì snapshot
save 300 100
appendonly yes
appendfsync everysec
aof-use-rdb-preamble yes
maxmemory 2gb
maxmemory-policy noeviction
Bước 3: Docker Compose mount volume để file không mất khi container bị xóa:
services:
redis:
image: redis:7.4
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
volumes:
- ./redis-store.conf:/usr/local/etc/redis/redis.conf:ro
- redis-data:/data
volumes:
redis-data:
Bước 4: Test
redis-cli SET session:abc '{"userId":42}'
docker compose restart redis
redis-cli GET session:abc # vẫn còn (AOF replay)
redis-cli INFO persistence | grep -E "aof_enabled|rdb_last_bgsave_status"
5. Cách hoạt động khi restart
Redis start
|
appendonly yes? ──yes──> load AOF (đầy đủ hơn)
| no
dump.rdb tồn tại? ──yes──> load RDB
| no
khởi động rỗng
Khi bật cả hai, Redis ưu tiên AOF vì đầy đủ hơn. Với aof-use-rdb-preamble, file base của AOF có dạng RDB nên load nhanh.
6. So sánh
| Tiêu chí | RDB | AOF |
|---|---|---|
| Bản chất | Snapshot | Log write command |
| Mất dữ liệu khi crash | Từ lần snapshot cuối (phút) | ~1 giây với everysec |
| Kích thước file | Nhỏ, compact | Lớn hơn, cần rewrite |
| Tốc độ restore | Nhanh | Chậm hơn (replay) |
| Chi phí | fork định kỳ, tốn RAM copy-on-write | fsync liên tục |
| Hợp với | Backup, cache có thể mất | Dữ liệu cần bền hơn |
7. Khi nào nên dùng / không nên dùng
- Chỉ cache: tắt persistence hoặc chỉ RDB để restart ấm cache nhanh.
- Session, token, counter quan trọng: AOF everysec + RDB, có replica (bài 7.4).
- Không nên coi Redis là primary store cho dữ liệu tiền, đơn hàng: AOF everysec vẫn có thể mất ~1 giây, replication lại async.
Đã có database rồi thì sao Redis còn cần persistence nhỉ?
Câu này phụ thuộc vào việc Redis đang giữ gì:
- Chỉ là bản sao từ DB (cache product): không cần. Mất thì load lại. Persistence lúc này chỉ giúp warm restart: tránh DB nhận toàn bộ tải ngay sau khi Redis khởi động lại.
- Dữ liệu chỉ có ở Redis (session, refresh token, giỏ hàng chưa lưu DB, winner flash sale trước khi kịp ghi DB): Redis là nơi lưu duy nhất, nên cần persistence và replica.
Redis giữ gì?
├─ copy của DB → persistence: tùy chọn (warm restart)
└─ nguồn gốc → persistence: bắt buộc + replica + chấp nhận mất ~1s
Với flash sale ở bài 7.5, winner nên được đẩy về DB qua message càng sớm càng tốt, để Redis không phải là nơi lưu cuối cùng.
Nếu RAM của Redis đầy thì nó xử lý thế nào? Có tự xóa key không?
Tùy maxmemory-policy. Khi dùng chạm maxmemory, Redis làm theo policy:
| Policy | Hành vi | Hợp với |
|---|---|---|
noeviction (mặc định) | Không xóa, write trả lỗi OOM | Redis giữ dữ liệu không được mất (session) |
allkeys-lru | Xóa key ít dùng gần đây nhất trong mọi key | Cache thuần, phổ biến nhất |
allkeys-lfu | Xóa key ít được truy cập nhất | Cache có hot key ổn định |
volatile-lru / volatile-lfu | Chỉ xóa trong các key có TTL | Trộn cache (có TTL) và dữ liệu cần giữ (không TTL) |
volatile-ttl | Xóa key sắp hết hạn trước | Ít dùng |
allkeys-random / volatile-random | Xóa ngẫu nhiên | Ít dùng |
LRU/LFU trong Redis là xấp xỉ: lấy mẫu vài key (maxmemory-samples) rồi chọn key tệ nhất, không duy trì danh sách chính xác.
Gợi ý cho shop-app: tách hai Redis (hoặc hai database logic, tốt hơn là hai instance): một cho cache với allkeys-lru, một cho session/lock với noeviction. Trộn chung với allkeys-lru thì session có thể bị xóa âm thầm.
Phỏng vấn hỏi RDB và AOF khác nhau thế nào, Redis đầy RAM thì sao, trả lời thế nào?
Q1. Restart Redis có mất data không? RDB và AOF khác nhau thế nào?
🇻🇳 Tiếng Việt:
Không nhất thiết, Redis có persistence. RDB là snapshot định kỳ, file nhỏ, restore nhanh, nhưng mất dữ liệu từ lần snapshot cuối. AOF ghi log mọi write command; với appendfsync everysec chỉ mất khoảng 1 giây, đổi lại file lớn hơn và cần rewrite. Production thường bật cả hai.
🇬🇧 English:
Not necessarily, because Redis has persistence. RDB takes periodic snapshots; the file is compact and restores quickly, but you lose everything since the last snapshot. AOF logs every write command; with appendfsync everysec you lose at most about one second, at the cost of a larger file that needs rewriting. In production we usually enable both.
Q2. Có dùng Redis làm primary database được không?
🇻🇳 Tiếng Việt:
Được với dữ liệu chấp nhận mất một ít như session, counter, leaderboard. Với dữ liệu tài chính hay đơn hàng thì em không dùng, vì AOF everysec vẫn có thể mất khoảng 1 giây, replication là async, và không có query phức tạp hay ràng buộc như DB quan hệ.
🇬🇧 English:
Yes for data that can tolerate a small loss, such as sessions, counters or leaderboards. For financial data or orders I would not, because AOF with everysec can still lose about a second, replication is asynchronous, and Redis lacks the rich queries and constraints of a relational database.
Q3. Redis đầy RAM thì sao?
🇻🇳 Tiếng Việt:
Phụ thuộc maxmemory-policy. Mặc định noeviction thì write bị lỗi. Redis làm cache em đặt allkeys-lru hoặc allkeys-lfu để tự xóa key ít dùng. Redis giữ session thì để noeviction và tách khỏi instance cache.
🇬🇧 English:
It depends on maxmemory-policy. With the default noeviction, writes start failing. For a cache I set allkeys-lru or allkeys-lfu so rarely used keys are evicted. For session data I keep noeviction and run it on a separate instance from the cache.
Cách nhớ ngắn gọn
- RDB = snapshot (mất phút); AOF = write log (mất ~1s với everysec).
- Redis giữ copy của DB → persistence tùy chọn; giữ bản gốc → bắt buộc.
- Cache:
allkeys-lru; session/lock:noeviction, tách instance.
Tự kiểm tra
RDB tạo snapshot mà không dừng Redis bằng cách nào?
fork() process con ghi file; process cha tiếp tục phục vụ nhờ copy-on-write.
appendfsync everysec có thể mất bao nhiêu dữ liệu?
Khoảng 1 giây write gần nhất.
Bật cả RDB và AOF thì khi restart Redis load file nào?
AOF, vì đầy đủ hơn.
Vì sao không nên trộn session và cache trong một Redis allkeys-lru?
Khi đầy RAM, session có thể bị evict, user bị logout âm thầm.
noeviction làm gì khi đầy?
Không xóa key, trả lỗi OOM cho write command; read vẫn chạy.