Persistence: RDB, AOF và eviction policy

4 câu hỏi~25 phútTừ khóa: RDB snapshot, BGSAVE, fork, AOF, appendfsync, AOF rewrite, data loss window, maxmemory, maxmemory-policy, allkeys-lru, volatile-ttl, noeviction

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:

appendfsyncKhi nào fsync xuống diskCó thể mấtHiệu năng
alwaysMỗi writeGần như khôngChậm nhất
everysec (mặc định)Mỗi giây~1 giây dữ liệuTốt, cân bằng
noĐể OS quyết định (~30s)Vài chục giâyNhanh 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íRDBAOF
Bản chấtSnapshotLog write command
Mất dữ liệu khi crashTừ lần snapshot cuối (phút)~1 giây với everysec
Kích thước fileNhỏ, compactLớn hơn, cần rewrite
Tốc độ restoreNhanhChậm hơn (replay)
Chi phífork định kỳ, tốn RAM copy-on-writefsync liên tục
Hợp vớiBackup, cache có thể mấtDữ liệu cần bền hơn

7. Khi nào nên dùng / không nên dùng

Đã 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ì:

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:

PolicyHành viHợp với
noeviction (mặc định)Không xóa, write trả lỗi OOMRedis giữ dữ liệu không được mất (session)
allkeys-lruXóa key ít dùng gần đây nhất trong mọi keyCache thuần, phổ biến nhất
allkeys-lfuXóa key ít được truy cập nhấtCache có hot key ổn định
volatile-lru / volatile-lfuChỉ xóa trong các key có TTLTrộn cache (có TTL) và dữ liệu cần giữ (không TTL)
volatile-ttlXóa key sắp hết hạn trướcÍt dùng
allkeys-random / volatile-randomXó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


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.