RDB là cách lưu dữ liệu mặc định của Redis: chụp toàn bộ bộ nhớ ra một tệp. Bài này đo nó tốn gì — và tìm ra khoản chi phí mà chỉ số của Redis không hiển thị.
BGSAVE trên 2 triệu khoá
thời gian BGSAVE 1,21 giây
tệp RDB trên đĩa 46.889.287 byte = 46,9 MB
dữ liệu trong bộ nhớ 337,94 MB
Tệp nhỏ hơn bộ nhớ 7,2 lần. RDB là định dạng nhị phân nén chặt: không con trỏ, không bảng băm, không phần phụ trội 64 byte mỗi khoá đo được ở phần 3.
Độ trễ của khách trong lúc chụp:
PING p99 1,695 ms, max 5,3 ms
Gần như không ảnh hưởng gì. Đó là nhờ fork: Redis tạo một tiến trình con và tiến trình con ghi từ bản sao đông cứng của bộ nhớ, còn tiến trình cha tiếp tục phục vụ.
Chi phí thật nằm ở copy-on-write
fork không sao chép bộ nhớ ngay — cả hai tiến trình dùng chung các trang cho tới khi một bên ghi. Lúc đó kernel sao chép trang đó ra.
Tôi đo rdb_last_cow_size ở hai điều kiện:
không ghi gì trong lúc chụp 1.429.504 byte = 1,4 MB
ghi đè 7.016.600 khoá 91.254.784 byte = 87 MB
Gấp 64 lần.
Đây là khoản chi phí quyết định việc bạn cần bao nhiêu RAM dư. Với hệ thống ghi nhiều và bộ dữ liệu lớn, phần sao chép có thể tiến gần tới kích thước toàn bộ dữ liệu — đó là nguồn gốc của lời khuyên "chỉ dùng 50% RAM cho Redis".
Và chỉ số của Redis không thấy khoản đó
used_memory_rss: 348,5 MB trước và sau, không đổi
Đây là chỗ tôi mất một lúc mới hiểu. rdb_last_cow_size báo 87 MB, nhưng RSS không nhúc nhích.
Lý do: những trang bị sao chép thuộc về tiến trình con. INFO memory chỉ đo tiến trình cha. Redis biết con số — nó báo trong rdb_last_cow_size — nhưng nó không cộng vào bất kỳ chỉ số bộ nhớ nào.
Nghĩa là: bảng điều khiển dựa trên used_memory_rss sẽ không bao giờ thấy đỉnh bộ nhớ lúc lưu. Muốn thấy phải nhìn ở tầng hệ điều hành, hoặc theo dõi chính rdb_last_cow_size.
Tôi cũng thử bắt đỉnh bộ nhớ ở mức container và không bắt được — BGSAVE xong trong 1,59 giây, ngắn hơn chu kỳ lấy mẫu. Trên bộ dữ liệu hàng chục GB, cửa sổ đó dài hơn nhiều và đỉnh sẽ thấy rõ.
Nạp lại khi khởi động
rdb_last_load_keys_loaded 2.000.000
thời gian nạp khoảng 2,5 – 3 giây
Trong suốt thời gian đó, mọi lệnh nhận:
LOADING Redis is loading the dataset in memory
Redis không phục vụ gì cho tới khi nạp xong. Với bộ dữ liệu 50 GB, đó là vài phút không có dịch vụ sau mỗi lần khởi động lại.
Con số đáng ước lượng trước: khoảng 700.000 khoá mỗi giây ở phép đo này. Chia số khoá của bạn cho con số đó để biết thời gian khởi động lại — và so nó với thời gian chịu đựng được.
Quy tắc save mặc định là một lời hứa mỏng
save 3600 1 300 100 60 10000
Đọc là: lưu nếu 3600 giây qua có ít nhất 1 thay đổi, hoặc 300 giây qua có 100 thay đổi, hoặc 60 giây qua có 10.000 thay đổi.
Dòng đầu là chỗ đáng chú ý: hệ thống ghi ít có thể mất tới một giờ thay đổi.
Và ngay cả dòng cuối cũng chỉ hứa 60 giây. RDB về bản chất là "mất một khoảng gần đây" — nó không phải cơ chế cho dữ liệu không được mất. Phần sau đo AOF, thứ dựng cho đúng nhu cầu đó.
Bốn điều nên biết
SAVE chặn, BGSAVE thì không. SAVE chụp trên luồng chính — với 338 MB là hơn một giây không phục vụ ai. Đừng gõ nó trên production; nó có mặt cho những trường hợp không fork được.
fork thất bại nếu không đủ RAM. Kernel cần cấp phát bảng trang cho tiến trình con. Với vm.overcommit_memory=0 (mặc định trên nhiều bản Linux), fork có thể bị từ chối và BGSAVE thất bại im lặng — chỉ hiện trong rdb_last_bgsave_status. Redis khuyến nghị đặt vm.overcommit_memory=1 chính vì lý do này.
Trang lớn làm copy-on-write tệ hơn nhiều. Với transparent huge pages, mỗi lần ghi sao chép 2 MB thay vì 4 KB — gấp 512 lần. Redis in cảnh báo lúc khởi động nếu phát hiện; nên tắt.
Kiểm rdb_last_bgsave_status chứ đừng chỉ tin có tệp. Tệp cũ vẫn nằm đó sau một lần lưu thất bại, và dấu thời gian của nó không nói cho bạn biết lần gần nhất thành công hay không.
Thử ba mươi giây
Kiểm ba thứ quyết định bạn mất bao nhiêu khi Redis tắt đột ngột:
redis-cli config get save
redis-cli info persistence | grep -E \
'rdb_changes_since_last_save|rdb_last_bgsave_status|rdb_last_cow_size|rdb_last_bgsave_time_sec'
Cách đọc:
rdb_changes_since_last_save— chính xác số thay đổi sẽ mất nếu Redis chết ngay bây giờ. Con số này là câu trả lời trực tiếp nhất cho câu hỏi "chúng ta mất gì".rdb_last_bgsave_statuskhácok— bản lưu gần nhất hỏng, và bạn đang không có bản sao lưu nào mới.rdb_last_cow_sizegần bằngused_memory— mỗi lần lưu đang cần gần gấp đôi RAM, và bạn đang tiến gần tới lúcforkthất bại.
Phần sau: AOF — đo chính xác bao nhiêu dữ liệu mất ở từng mức appendfsync.