Bỏ qua để đến nội dung
Knowledge Hub

Redis persistence: restart hoặc kill -9 thì mất bao nhiêu dữ liệu? Trung cấp

Thời gian đọc ước tính: 8 phút

Một Redis 7.4 giữ cache sản phẩm (nạp lại được từ PostgreSQL), session đăng nhập và bộ đếm rate limit theo IP. Tất cả nằm trong RAM; persistence là cách Redis ghi dataset xuống đĩa để nạp lại khi khởi động.

Khi container bị kill -9 hoặc máy mất điện: mất cache chỉ tăng tải database, mất session thì người dùng bị đăng xuất, mất bộ đếm thì một IP được thêm một cửa sổ request.

Câu hỏi: với cấu hình hiện tại, sau crash Redis còn lại gì và mất tối đa bao nhiêu?

Tình huống Chọn Điều kiện
Cache thuần Tắt persistence (save "", appendonly no) hoặc RDB thưa Database chịu được cold start; có replica thì master không được tự restart
Session, bộ đếm rate limit AOF everysec + giữ RDB Chấp nhận mất khoảng 1 s ghi khi máy sập
Dữ liệu không được mất Không để Redis là nguồn sự thật duy nhất; nếu bắt buộc: AOF always + replication + WAIT Throughput giảm mạnh; failover vẫn có thể mất ghi đã xác nhận

Docs Redis khuyên bật cả hai khi cần độ an toàn gần PostgreSQL; RDB vẫn hữu ích cho backup và restart nhanh.

RDB AOF
Ghi gì Snapshot toàn dataset (dump.rdb) Log mọi lệnh ghi, replay khi khởi động
Khi nào Rule save <giây> <số thay đổi> hoặc BGSAVE Mỗi lệnh ghi; fsync theo appendfsync
Mất khi crash Mọi ghi sau snapshot cuối Tuỳ appendfsync (bảng dưới)
Default 7.4 Bật: save 3600 1 300 100 60 10000 Tắt: appendonly no

Rule save mặc định: snapshot sau 3600 s nếu ≥ 1 thay đổi, sau 300 s nếu ≥ 100, sau 60 s nếu ≥ 10000. Kiểm tra bằng CONFIG GET save.

fsync buộc kernel đẩy dữ liệu từ page cache xuống đĩa. Đã write() mà chưa fsync thì dữ liệu sống sót khi process chết, nhưng mất khi máy sập.

appendfsync Hành vi (theo docs) Mất khi máy sập
always fsync trước khi trả lời client, gộp nhiều lệnh vào một fsync Gần như không; rất chậm
everysec (default) fsync mỗi giây ở thread nền Khoảng 1 s; tới 2 s nếu fsync chậm khiến Redis hoãn write()
no Kernel tự flush (Linux thường khoảng 30 s) Tuỳ kernel

Session B và một lệnh INCR đi qua RAM, dump.rdb, thư mục AOF, rồi Redis chết:

Sau crash, RDB quay về snapshot cuối; AOF everysec chỉ hở phần chưa fsync
Sau crash, RDB quay về snapshot cuối; AOF everysec chỉ hở phần chưa fsyncBước 1/7: Snapshot gần nhất chứa session A. Từ đây RDB đứng yên tới lần save sau.thời gianBGSAVE xongcó session AỨng dụngsession + rate limitRedis memorydatasetdump.rdbsave 60 1000appendonlydirappendfsync everysec
Bước 1/7

Snapshot gần nhất chứa session A. Từ đây RDB đứng yên tới lần save sau.

Mốc Memory dump.rdb AOF trên đĩa
Snapshot A A A
SET B, fsync A, B A A, B
INCR A, B, rl=1 A INCR chưa fsync
Restart Chỉ RDB: còn A A, B; INCR còn nếu chỉ process chết

BGSAVE gọi fork(). Process con ghi snapshot ra file tạm rồi rename đè file cũ, nên dump.rdb không bao giờ ghi dở; process cha tiếp tục phục vụ client.

Copy-on-write: sau fork, cha và con dùng chung trang nhớ; trang nào cha sửa thì kernel mới chép riêng. Ghi càng nhiều trong lúc save, RAM càng tăng. Hai thứ cần đo:

  • Latency fork: fork() chép page table trên main thread. Xem latest_fork_usec (INFO stats); docs ghi khoảng 9–13 ms/GB trên phần cứng hiện đại.
  • Bộ nhớ CoW: log Fork CoW for RDB: ... và rdb_last_cow_size trong INFO persistence. Docs yêu cầu tắt transparent huge pages vì THP làm gần như toàn bộ memory bị chép.

INCR 100 lần sinh 100 entry cho một key. Rewrite tạo lại tập lệnh tối thiểu: chạy bằng BGREWRITEAOF hoặc tự động khi AOF gấp đôi kích thước sau lần rewrite trước và ≥ 64 MB (auto-aof-rewrite-percentage 100, auto-aof-rewrite-min-size 64mb).

Từ 7.0, AOF là thư mục appenddirname (mặc định appendonlydir):

  • base: snapshot lúc rewrite; dạng RDB (.base.rdb) vì aof-use-rdb-preamble yes là default.
  • incr: lệnh ghi sau base (.incr.aof).
  • manifest: danh sách file và thứ tự nạp.

Khi rewrite, cha ghi tiếp vào incr mới, con viết base mới, rồi Redis thay manifest nguyên tử; rewrite lỗi thì file cũ vẫn đủ dữ liệu.

Đang tải diagram…

Docs: cả hai cùng bật thì nạp AOF vì đầy đủ nhất. Nhánh “AOF rỗng” đã tái hiện trong demo.

  • Shutdown sạch (SIGTERM): có rule save thì Redis ghi RDB trước khi thoát (demo: docker stop còn đủ 5000/5000 key). docker stop chỉ chờ 10 s rồi SIGKILL.
  • stop-writes-on-bgsave-error yes (default): BGSAVE lỗi, ví dụ đầy đĩa, thì Redis từ chối ghi.
  • aof-load-truncated yes (default): AOF cụt ở cuối vẫn nạp; hỏng ở giữa thì cần redis-check-aof.

Docs replication mô tả: master tắt persistence, crash, được tự restart với dataset rỗng; replica đồng bộ lại và xoá luôn bản sao. Với Sentinel, master có thể restart nhanh tới mức không bị phát hiện.

Khuyến nghị: bật persistence trên master và replica; nếu không thể thì tắt auto-restart cho master. Replication bất đồng bộ, WAIT chỉ giảm xác suất mất ghi.

Demo: Redis 7.4.9 riêng (port 6391), ghi lô 50 SET liên tục 3 giây, docker kill ngay sau lô được xác nhận cuối, restart, đếm DBSIZE:

Terminal window
npx --prefix examples ts-node examples/src/redis-persistence/crash-demo.ts rdb # cả everysec, always, none
Cấu hình Client nhận OK Còn sau restart Mất
Không persistence 589.800 0 589.800
RDB save 60 1000, snapshot ở giây 60 1.058.300 411.550 646.750
AOF everysec 630.950 630.950 0
AOF always 183.850 183.850 0

Đọc kết quả:

  • docker kill chỉ giết process; dữ liệu đã write() còn trong page cache nên everysec mất 0. Mất điện mới lộ phần chưa fsync; demo không mô phỏng được.
  • always nhận ít OK hơn everysec khoảng 3,4 lần (Docker Desktop trên macOS, chỉ so tương đối; số đổi mỗi lần chạy).
Output thật: thư mục AOF, bật AOF sai cách, fork và CoW
[everysec] sau restart: 630950 key còn, mất 0 key
-rw------- 1 redis redis 22183987 appendonly.aof.2.base.rdb
-rw------- 1 redis redis 33363023 appendonly.aof.2.incr.aof
-rw------- 1 redis redis 88 appendonly.aof.manifest
file appendonly.aof.2.base.rdb seq 2 type b
file appendonly.aof.2.incr.aof seq 2 type i

Restart với --appendonly yes trên volume có dump.rdb 5000 key:

* Creating AOF base file appendonly.aof.1.base.rdb on server start
DBSIZE → 0

Dataset 383 MB, BGSAVE trong khi redis-benchmark ghi SET ngẫu nhiên:

latest_fork_usec:2475
Fork CoW for RDB: current 108 MB, peak 108 MB, average 72 MB

Persistence đem lại gì

  • RDB: file gọn, restart nhanh, chép backup an toàn khi Redis đang chạy
  • AOF everysec: process chết gần như không mất ghi; máy sập mất khoảng 1 s
  • Multi-part AOF: rewrite không đệm ghi trong RAM, base dạng RDB nạp nhanh

Cái giá phải trả

  • Fork chặn main thread; CoW cần RAM dự phòng
  • AOF always giảm throughput nhiều lần
  • fsync chậm có thể gây spike latency
  1. Tách instance cache thuần khỏi instance giữ session.
  2. Session, bộ đếm: appendonly yes, appendfsync everysec, giữ rule save; kiểm tra bằng CONFIG GET sau deploy.
  3. Dự phòng RAM cho CoW, tắt transparent huge pages, cảnh báo theo latest_fork_usec và rdb_last_cow_size.
  4. Cảnh báo khi rdb_last_bgsave_status, aof_last_bgrewrite_status, aof_last_write_status khác ok.
  5. Đổi RDB sang AOF bằng CONFIG SET appendonly yes, chờ rewrite xong, CONFIG REWRITE, rồi mới restart.
  6. Có replica: bật persistence trên master hoặc tắt auto-restart cho master.
  7. Timeout dừng (docker stop -t, terminationGracePeriodSeconds) đủ cho lần save cuối.
  8. Chép RDB hoặc thư mục AOF ra ngoài máy định kỳ và thử restore.

Bẫy thứ hai nằm ở chỗ ghép replication với master không persistence:

Cách hiểu sai Hệ quả Cần làm
Bật cả hai thì Redis nạp file mới hơn Khôi phục nhầm Có AOF thì Redis luôn nạp AOF
always nghĩa là không bao giờ mất Vẫn mất khi failover sang replica chưa nhận ghi Nguồn sự thật ở database có transaction
  1. Chỉ RDB, rule default, ghi 50 thay đổi lúc 10:00, kill -9 lúc 10:04. Restart còn gì?
  2. Vì sao docker kill với everysec mất 0 key, còn mất điện có thể mất khoảng 1 giây?
  3. Dataset 20 GB ghi dày lúc BGSAVE: đo RAM dự phòng bằng metric nào?
  4. Vì sao replica bật AOF vẫn có thể mất dữ liệu?