Phần trước đo giá của việc nhìn. Phần này đo giá của việc giữ: một mẫu chiếm bao nhiêu byte sau khi nén, giãn chu kỳ thu thập tiết kiệm được bao nhiêu, và — câu hỏi ít ai đặt — giảm mẫu để tiết kiệm chỗ thì bạn mất đúng cái gì.
Bảng số liệu
Ba Prometheus 2.54.1 chạy song song, cùng thu thập một exporter duy nhất, khác nhau đúng một tham số là chu kỳ thu thập. Chạy 180 giây.
Nén, đo trên bản chu kỳ 1 giây (3 881 303 mẫu):
| Dung lượng | Byte mỗi mẫu | |
|---|---|---|
| WAL — nhật ký ghi trước, chưa nén | 26,84 MB | 7,25 |
| Chunk — đã nén | 8,00 MB | 2,16 |
Nén được 3,36 lần.
Đổi chu kỳ thu thập — số chuỗi giống hệt nhau ở cả ba:
| Chu kỳ | Số chuỗi | Số mẫu | Tổng thư mục | So với 15s |
|---|---|---|---|---|
| 1 giây | 21 563 | 3 881 303 | 34,87 MB | 17,3× |
| 5 giây | 21 563 | 754 668 | 5,55 MB | 2,7× |
| 15 giây | 21 563 | 258 168 | 2,02 MB | 1,0× |
Giảm mẫu — một chuỗi 3 600 điểm với ba đợt chậm, đỉnh cao nhất 939,3 ms, gộp 60 điểm thành 1:
| Cách gộp | Đỉnh còn lại | Giữ được |
|---|---|---|
Trung bình (avg) |
149,9 ms | 16% |
Nhỏ nhất (min) |
13,7 ms | 1% |
| p95 trong cửa sổ | 850,8 ms | 91% |
Lớn nhất (max) |
939,3 ms | 100% |
Một đợt chậm phải kéo dài bao lâu thì avg cửa sổ 60 giây còn thấy:
| Độ dài đợt | avg |
So với nền |
|---|---|---|
| 1 giây | 31,5 ms | 1,5× |
| 5 giây | 84,6 ms | 4,0× |
| 10 giây | 146,4 ms | 6,9× |
| 30 giây | 406,7 ms | 19,2× |
Điều đáng nhớ
Nói lại theo cách khác:
Một mẫu chỉ tốn 2,16 byte. Đó là một cặp dấu thời gian và giá trị — về lý thuyết 16 byte — được nén xuống còn hơn một phần bảy. Prometheus không lưu giá trị mà lưu độ lệch so với điểm trước, và với chuỗi thu thập đều thì độ lệch của dấu thời gian gần như luôn bằng nhau nên gần như miễn phí.
Giãn chu kỳ thu thập là đòn bẩy gần như tuyến tính. Từ 1 giây sang 15 giây: số mẫu giảm 15,0 lần, dung lượng giảm 17,3 lần. Vì số chuỗi giống hệt nhau ở cả ba bản, toàn bộ chênh lệch đến từ số mẫu — nghĩa là ở quy mô này chi phí của từng mẫu chi phối, chứ không phải chi phí cố định của từng chuỗi.
Nhưng giảm mẫu thì không miễn phí, và cái giá không nằm ở chỗ người ta nghĩ. Gộp 60 điểm thành một bằng trung bình giữ lại đúng 16% chiều cao đỉnh: một đợt chậm 939 ms hiện ra thành 150 ms. Dữ liệu vẫn còn, biểu đồ vẫn có đường, chỉ là đỉnh đã bị san phẳng và không ai biết.
Đợt càng ngắn thì trung bình càng mù. Một đợt chậm kéo dài 1 giây trong cửa sổ 60 giây chỉ làm trung bình nhích lên 1,5 lần nền — nằm trong nhiễu. Phải kéo dài ít nhất 30 giây thì nó mới rõ ràng.
Vì sao
Nén của Prometheus dựa trên hai quan sát về dữ liệu chuỗi thời gian: dấu thời gian tăng đều, và giá trị thường thay đổi ít giữa hai mẫu liền nhau. Nó lưu độ lệch của độ lệch cho dấu thời gian — với chu kỳ đều thì con số đó là 0 và chỉ tốn vài bit — và lưu phần khác biệt nhị phân cho giá trị. Kết quả là 2,16 byte cho một mẫu mà biểu diễn thô cần 16 byte.
Điều đó cũng giải thích vì sao con số này không phải hằng số: chuỗi càng đều thì càng nén tốt. Một bộ đếm tăng chậm nén cực tốt; một chỉ số nhảy ngẫu nhiên thì tệ hơn nhiều.
Còn chuyện giảm mẫu mất đỉnh là số học đơn giản. Trong cửa sổ 60 giây có 10 giây chậm ở 800 ms và 50 giây bình thường ở 20 ms, trung bình là (50 × 20 + 10 × 800) / 60 ≈ 150 ms. Trung bình làm đúng việc của nó — nó tính giá trị đại diện — và giá trị đại diện của một cửa sổ chủ yếu bình thường thì phải gần mức bình thường.
Đây là cùng một cơ chế đã thấy ở phần 12 khi trung bình che mất đuôi phân bố, chỉ khác là lần này nó che theo trục thời gian thay vì theo trục phân vị. Và nó nguy hiểm hơn, vì đây là phép biến đổi không đảo ngược được: khi dữ liệu độ phân giải cao đã bị xoá, không cách nào lấy lại đỉnh.
Nghĩa là gì trong thực tế
- Ước lượng dung lượng bằng 2,16 byte mỗi mẫu, và nhân ra. 21 563 chuỗi thu thập mỗi 15 giây là 1 438 mẫu mỗi giây, tức 5,18 triệu mỗi giờ và khoảng 256 MB mỗi ngày. Con số đó còn dễ chịu — cho tới khi số chuỗi tăng gấp mười, và lúc đó là 2,5 GB mỗi ngày cho đúng một chỉ số.
- Giãn chu kỳ trước khi nghĩ tới giảm mẫu. Đổi từ 15 giây sang 30 giây cắt một nửa dung lượng và không mất gì ngoài độ phân giải thời gian — trong khi giảm mẫu bằng trung bình cắt được nhiều hơn nhưng xoá mất thông tin về đỉnh.
- Nếu giảm mẫu, hãy giữ
maxsong song vớiavg. Giữ cảavg,maxvàmincho ra 180 điểm thay vì 3 600 — vẫn giảm 20 lần thay vì 60 lần, và đó là cái giá hợp lý để không mù trước sự cố ngắn. Các hệ lưu trữ dài hạn quen thuộc đều làm đúng như vậy, và giờ bạn biết vì sao. - Đừng nhìn WAL rồi tính ngân sách đĩa. WAL là 26,84 MB trong khi dữ liệu thật là 8,00 MB — chênh 3,36 lần. Đây là lần thứ hai trong sê-ri này thư mục ghi-trước suýt làm hỏng một kết luận về dung lượng.
- Khi đọc biểu đồ dữ liệu cũ, hãy nhớ nó có thể đã bị giảm mẫu. Một biểu đồ sáu tháng trông phẳng lặng không có nghĩa là sáu tháng qua không có sự cố nào — nó có thể nghĩa là mọi sự cố đều ngắn hơn cửa sổ gộp.
Chỗ tôi không kết luận được
Ba bản Prometheus của tôi chạy quá ngắn để so nén một cách công bằng. Prometheus chỉ ghi một chunk xuống đĩa khi chunk đầy 120 mẫu, nên sau 180 giây chỉ bản chu kỳ 1 giây có chunk thật; hai bản còn lại có chunks = 0,00 MB và toàn bộ dung lượng của chúng là WAL. Vì thế cột "tổng thư mục" trong bảng so WAL với WAL chứ không so dữ liệu đã nén. Tỷ lệ 17,3 lần vẫn phản ánh đúng chiều hướng vì cả ba đều ở cùng trạng thái, nhưng con số nén 2,16 byte mỗi mẫu thì chỉ đo được trên bản 1 giây. Muốn so đúng, phải chạy bản 15 giây ít nhất 30 phút.
Tỷ lệ nén phụ thuộc dữ liệu. Exporter của tôi tăng bộ đếm bằng số ngẫu nhiên trong khoảng 0–50 mỗi lần, tức tương đối đều. Chỉ số thật có cái đều hơn (bộ đếm request) và cái nhảy loạn hơn (độ trễ, độ sâu hàng đợi). Con số 2,16 nằm ở khoảng giữa chứ không phải một trần hay sàn.
Phần giảm mẫu là tính toán trên dữ liệu tôi sinh ra, không phải một hệ lưu trữ dài hạn thật. Tôi không chạy Thanos hay Mimir; tôi lấy một chuỗi và tự gộp nó bằng bốn hàm khác nhau. Điều đó đủ để chứng minh trung bình xoá đỉnh — đó là số học, không phụ thuộc công cụ — nhưng nó không nói gì về chi phí và hành vi thật của các hệ đó.
Đỉnh trong dữ liệu thử là do tôi đặt vào: ba đợt, mỗi đợt 10 giây, cao gấp khoảng 40 lần nền. Chọn đợt dài hơn hoặc thấp hơn thì tỷ lệ "giữ được 16%" sẽ khác. Cái tôi tin là cơ chế và bảng độ dài đợt ở cuối — nó cho thấy chính xác ngưỡng mà trung bình bắt đầu nhìn thấy.
Thử ba mươi giây
docker run --rm python:3.12-slim python - <<'EOF'
import random, statistics
random.seed(5)
lat = [random.lognormvariate(-3.9, 0.25) for _ in range(3600)] # nen ~20 ms
for s in (600, 1800, 3000): # ba dot cham
for i in range(s, s+10): lat[i] = random.lognormvariate(-0.25, 0.10)
def gop(f, w=60): return [f(lat[i:i+w]) for i in range(0, len(lat), w)]
dinh = max(lat)
print("dinh that %8.1f ms" % (dinh*1000))
for ten, f in (("gop bang avg", statistics.fmean), ("gop bang max", max)):
print("%-20s %8.1f ms giu duoc %3.0f%%"
% (ten, max(gop(f))*1000, 100*max(gop(f))/dinh))
EOF
Hai dòng cuối là hai biểu đồ mà bạn sẽ nhìn sáu tháng sau. Một trong hai vẫn còn sự cố; cái kia thì không, và không có gì trên màn hình cho biết nó đã từng có.