Hai phần trước đo RDB và AOF riêng lẻ. Bài này đặt chúng cạnh nhau trên cùng 2 triệu khoá và đo thứ quan trọng nhất: thời gian sống lại sau sự cố.

So sánh kích thước tệp, thời gian nạp lại, và khác biệt về lượng dữ liệu mất

Kích thước tệp chênh 5,8 lần

RDB                    46.888.988 byte
AOF có tiền tố RDB     46.893.128 byte    gần như y hệt
AOF văn bản thuần     270.893.097 byte    lớn hơn 5,8 lần

Hai dòng đầu gần bằng nhau, và đó không phải trùng hợp. Từ Redis 7, aof-use-rdb-preamble mặc định yes, và tệp cơ sở của AOF thật sự là một tệp RDB:

appendonly.aof.4.base.rdb
appendonly.aof.4.incr.aof
appendonly.aof.manifest

Sau mỗi lần viết lại, Redis chụp trạng thái hiện tại dưới dạng RDB rồi nối các lệnh mới vào tệp .incr.aof. Nghĩa là AOF hiện đại không phải một tệp văn bản khổng lồ; nó là RDB cộng thêm phần đuôi.

Dòng thứ ba là AOF kiểu cũ, khi tôi tắt tiền tố RDB: 270 MB cho cùng dữ liệu.

Nhưng thời gian nạp lại thì bằng nhau

Đây là chỗ tôi trông đợi thấy khác biệt lớn và không thấy.

Thời gian nạp
RDB (46,9 MB) 1,416 / 1,331 / 1,343 giây
AOF văn bản (270,9 MB) 1,399 / 1,321 / 1,296 / 1,392 giây
AOF + tiền tố RDB 1,509 giây

Tệp lớn gấp 5,8 lần mà nạp nhanh bằng.

Lý do: thời gian nạp bị chi phối bởi việc dựng 2 triệu mục trong bảng băm, không bởi việc đọc byte. Cấp phát bộ nhớ, băm khoá, chèn vào bảng, thỉnh thoảng mở rộng bảng — đó mới là công việc. Phân tích 270 MB văn bản so với 47 MB nhị phân chỉ là phần nhỏ.

Con số đọc được từ nhật ký của chính Redis (DB loaded from disk: X seconds), không phải đo từ ngoài, nên không lẫn thời gian khởi động container.

Lời khuyên quen thuộc "AOF nạp chậm hơn RDB nhiều" không đúng ở quy mô này. Nó có thể đúng với một tệp AOF chưa từng được viết lại — một tệp chứa hàng chục triệu lệnh chồng lên nhau trên cùng vài khoá, vì lúc đó Redis phải chạy lại tất cả. Nhưng đó là vấn đề của việc không viết lại, không phải của định dạng.

Khác biệt thật nằm ở lượng mất

Tốc độ khôi phục gần như nhau, nên lựa chọn không nằm ở đó.

Mất tối đa
RDB mặc định (save 3600 1) một giờ thay đổi
RDB (save 60 10000) 60 giây, nếu đủ 10.000 thay đổi
AOF everysec 1 giây khi mất điện
AOF everysec hoặc no 0 khi tiến trình bị SIGKILL (đo ở phần 19)

Đây mới là bảng quyết định. RDB một mình là "mất một khoảng gần đây, khoảng đó có thể rất dài".

Bật cả hai

Redis cho phép, và đó là cấu hình đúng cho phần lớn trường hợp:

appendonly yes
appendfsync everysec
save 3600 1 300 100 60 10000

Khi khởi động, nếu appendonly yes thì Redis luôn nạp từ AOF và bỏ qua RDB — AOF mới hơn.

Vậy giữ RDB làm gì? Vì nó là một tệp duy nhất, gọn, chép đi được:

  • dump.rdb mang sang máy khác, đưa vào kho sao lưu, nén lại.
  • AOF là một thư mục nhiều tệp kèm tệp kê khai; sao chép nửa vời là hỏng.

Nên: AOF để khôi phục tại chỗ, RDB để mang đi.

Chi phí của việc bật cả hai là hai nguồn fork — và như đo ở phần 18, fork mang chi phí copy-on-write thật. Trên hệ thống ghi rất nhiều, cân nhắc tắt RDB tự động và chỉ gọi BGSAVE theo lịch vào lúc rảnh.

Ba tình huống, ba cấu hình

Bộ đệm thuần, mất hết cũng được: tắt cả hai.

save ""
appendonly no

Không fork, không ghi đĩa, không copy-on-write. Đây là cấu hình nhanh nhất và nhiều người quên rằng nó tồn tại.

Dữ liệu quan trọng: AOF everysec + RDB theo lịch. Cấu hình ở trên.

Cần khởi động lại thật nhanh: chỉ RDB, và chấp nhận mất. Nạp RDB không nhanh hơn AOF ở phép đo này, nhưng nó tránh được chi phí ghi AOF liên tục lúc chạy bình thường.

Điều cả hai đều không làm

Cả RDB lẫn AOF đều không bảo vệ bạn khỏi:

  • Lệnh FLUSHALL gõ nhầm — nó được ghi vào AOF và nhân bản sang bản sao ngay lập tức.
  • Đĩa hỏng — cả hai tệp nằm trên cùng ổ đó.
  • Máy chủ mất.

Cho ba thứ đó cần sao lưu ra ngoài máy: chép dump.rdb sang nơi khác theo lịch. Đây là chỗ RDB thắng rõ, vì nó là một tệp.

Và mẹo nhỏ với FLUSHALL: đổi tên nó đi.

rename-command FLUSHALL ""
rename-command FLUSHDB ""

Thử ba mươi giây

Xem bạn đang mất bao nhiêu nếu Redis chết ngay bây giờ:

redis-cli info persistence | grep -E \
  'aof_enabled|rdb_changes_since_last_save|rdb_last_save_time|aof_last_write_status'
date +%s

Lấy date +%s trừ rdb_last_save_time ra số giây kể từ lần lưu RDB gần nhất. Nếu aof_enabled:0 thì đó chính là lượng thời gian bạn sẽ mất.

Và kiểm thời gian khôi phục bằng chính nhật ký của Redis:

docker logs <container> 2>&1 | grep -i "DB loaded from disk"

Dòng đó có sẵn sau mỗi lần khởi động. Nó là con số thật cho cụm của bạn, chính xác hơn mọi ước lượng — và nếu bạn chưa từng nhìn nó, bây giờ là lúc tốt hơn nhiều so với lúc đang có sự cố.

Phần sau: nhân bản — đo độ trễ giữa bản chính và bản sao.