Bốn mươi phần, mỗi phần một phép đo chạy trong container rồi dọn đi. Bài này gom lại thành thứ dùng được.
Mười con số đáng nhớ nhất
| Con số | Ý nghĩa | Phần |
|---|---|---|
| 64 byte | phụ trội mỗi khoá chuỗi | 3 |
| 44 byte | ranh giới embstr sang raw |
3 |
| 2,6 byte | mỗi số trong intset — rẻ nhất trong Redis |
4 |
| ×18,6 | khi Set số nguyên vượt 512 phần tử | 4 |
| 14.384 byte | HyperLogLog, không tăng dù đếm bao nhiêu | 6 |
| ×19,7 | bộ nhớ khi tiêu thụ Stream quên XACK |
7 |
| ×44 | thông lượng khi thêm đường ống 64 | 2 |
| ×5.183 | bộ đệm đầu ra khi giá trị 10 MB thay vì 1 KB | 30 |
| 0 | bản ghi mất khi SIGKILL, kể cả appendfsync no |
19 |
| 79% | bản ghi mất khi bản sao chậm rồi máy chính chết | 21 |
Hai dòng cuối đứng cạnh nhau là bức tranh đầy đủ về độ bền của Redis: nó tốt hơn nhiều người nghĩ trong trường hợp thường, và tệ hơn nhiều người nghĩ trong trường hợp xấu.
Ba điều lặp lại ở nhiều phần nhất
1. Nút thắt thường là lượt đi về, không phải Redis.
Một kết nối không đường ống: 20.000 ops/s. Cùng kết nối đó với đường ống 64: 1.136.364 ops/s. Redis dùng chưa hết một nhân trong trường hợp đầu.
Điều này xuất hiện lại ở phần 12 (đường ống), 23 (chuyển hướng Cluster), 29 (khoá nóng không làm chậm), 32 (TLS miễn phí khi có đường ống). Trước khi tối ưu bất cứ thứ gì, hãy kiểm xem bạn có đang tốn một lượt đi về cho mỗi lệnh không.
2. Vượt một ngưỡng là nhảy bậc, không tăng dần.
512 trường Hash, 129 phần tử Set, 513 số nguyên, 45 byte giá trị, 10.001 kết nối — mỗi con số là một vách đá. Thêm một đơn vị và chi phí nhân lên 2 đến 18 lần.
Đây là lý do thử nghiệm ở quy mô nhỏ không dự đoán được hành vi ở quy mô lớn. Không có đường cong để ngoại suy; có bậc thang.
3. Redis hiếm khi báo lỗi. Nó chậm dần hoặc lớn dần.
Khoá không TTL, cache script không dọn, danh sách chờ Stream, chỉ mục tự dựng lệch, mã hoá đắt sau khi khoá đã nhỏ lại — không cái nào sinh ra một dòng lỗi. Chúng chỉ tích lại.
Hệ quả: giám sát Redis là giám sát xu hướng, không phải chờ báo động.
Danh sách kiểm trước khi đưa lên chạy thật
Cấu hình
-
maxmemoryđã đặt, khoảng 60–70% RAM khả dụng (18, 36) -
maxmemory-policyphù hợp —allkeys-lfucho bộ đệm, không đểvolatile-*khi không có TTL (9) -
requirepasshoặc ACL, vàdefaultđã tắt (32) -
bindkhông phải mọi giao diện; cổng không ánh xạ ra0.0.0.0(36) -
timeoutkhác 0 (31) -
appendonly yesnếu dữ liệu quan trọng (19, 20) -
client-output-buffer-limit normalđã đặt nếu có giá trị lớn (12, 30)
Mô hình dữ liệu
- Mọi khoá tạm có TTL — so
keysvớiexpirestrongINFO keyspace(8, 39) - Dùng Hash cho đối tượng, không một khoá mỗi trường (39)
- Kiểm
OBJECT ENCODINGcủa khoá lớn — có cái nào đang mang mã hoá đắt không cần thiết (5) - Thẻ băm
{}đủ hẹp nếu dùng Cluster (29) - Không giá trị nào trên 1 MB nếu tránh được (30)
Mã ứng dụng
- Bể kết nối, không mở kết nối mỗi lệnh (31)
- Đường ống hoặc
MGET/MSETcho thao tác hàng loạt (12) -
SET ... KEEPTTLkhi ghi đè khoá có hạn (8, 39) -
SCANthayKEYS,UNLINKthayDELcho khoá lớn (11) -
EVALSHAchứ khôngEVALlặp lại; script dùngKEYS/ARGV(14) - Tiêu thụ Stream có gọi
XACK, và có bộ dọn danh sách chờ (7) - Khoá phân tán kiểm giá trị trả về lúc giải phóng (24)
Vận hành
- Đã khôi phục thử từ RDB/AOF ít nhất một lần (20)
- Cảnh báo trên
evicted_keyschứ không chỉ trên phần trăm bộ nhớ (33) -
slowlog-log-slower-thanhạ xuống 1.000 µs (34) -
CLIENT SETNAMEtrong mọi ứng dụng (34) - Biết khoảng cách bản sao tính bằng byte, không chỉ
lag(21)
Ba lần tôi đo sai rồi phải sửa
Phần 2 — tôi chạy khách benchmark trong cùng container với Redis và đo được 181% CPU cho một máy chủ một luồng. Con số bất khả thi, và đó là cách tôi biết docker stats đang cộng cả máy chủ lẫn khách.
Phần 28 — tôi đo bốn chiến lược cập nhật bộ đệm và kết luận "xoá trước khi cập nhật" an toàn (0 lệch). Đo lại với người đọc song song thì nó để lại dữ liệu cũ vĩnh viễn trong một trên ba lần chạy. Phép đo đầu thiếu đúng thành phần làm lộ vấn đề.
Phần 36 — tôi đặt maxmemory 80mb trong container 128 MB và vẫn bị OOMKilled, tưởng đã tìm ra lỗi của Redis. Thực ra là tôi nạp dữ liệu bằng script Lua, và Redis chỉ kiểm maxmemory một lần ở đầu script — điều chính tôi đã đo ở phần 9.
Cả ba đều lộ ra vì cùng một lý do: một con số vượt qua giới hạn vật lý, hoặc không khớp với phép đo trước đó. Khi điều đó xảy ra, hãy nghi phép đo trước khi nghi hệ thống.
Điều còn lại sau bốn mươi phần
Redis là một bảng băm rất nhanh sống trong RAM, có sẵn mạng và vài kiểu dữ liệu dựng sẵn. Mọi thứ khác — độ bền, nhân bản, phân mảnh, truy vấn — là thứ được thêm vào xung quanh cái lõi đó, và mỗi thứ đều có một cái giá đo được.
Bốn mươi phép đo trong sê-ri này bác bỏ ba điều tôi tưởng là hiển nhiên khi bắt đầu: Redis không nhanh hơn PostgreSQL 100 lần (phần 1), appendfsync no không mất dữ liệu khi tiến trình bị giết (phần 19), và TLS không làm chậm khi có đường ống (phần 32).
Cụm của bạn khác cụm của tôi: khác phiên bản, khác phần cứng, khác hình dạng dữ liệu. Mọi con số ở đây nên coi là giả thuyết cần kiểm lại. Mỗi phần đều kết thúc bằng một lệnh chạy được — đó mới là phần quan trọng.
Thử ba mươi giây
Lệnh cuối của sê-ri, gộp năm thứ hay sai nhất:
redis-cli info | grep -E 'redis_version|used_memory_human|maxmemory_human|maxmemory_policy|evicted_keys|keyspace_hits|keyspace_misses|connected_clients|rdb_changes_since_last_save|aof_enabled'
echo "---"
redis-cli info keyspace
echo "---"
redis-cli config get requirepass timeout appendonly slowlog-log-slower-than | paste - -
echo "---"
redis-cli info commandstats | grep -E 'cmdstat_(keys|flushall|flushdb):' || echo "khong ai goi KEYS/FLUSH — tot"
Bốn khối, bốn câu hỏi: bộ nhớ có trần chưa, dữ liệu có tự dọn không, có ai vào được không, và có ai đang gọi lệnh nguy hiểm không.
Chạy nó trên hệ thống của bạn ngay bây giờ. Với mỗi dòng bất thường, sê-ri này có một phần đo được hậu quả cụ thể của nó.
Cảm ơn bạn đã theo hết bốn mươi phần.