Harbor

Giám sát metrics

KhóTime-seriesGhi rất nhiềuAlertingCardinalityGiống Datadog, Prometheus + Grafana

Hiểu đề

Giám sát metrics là gì? Hệ thống thu thập số đo (CPU, RAM, số request, độ trễ, lỗi) từ server và ứng dụng, lưu theo thời gian, vẽ dashboard và báo động khi có bất thường. Người dùng là kỹ sư vận hành, dev on-call và đội SRE.

Yêu cầu chức năng

  1. Server và ứng dụng gửi lên (hoặc cho phép thu thập) metrics dạng counter, gauge, histogram kèm label
  2. Người dùng truy vấn và vẽ dashboard theo khoảng thời gian, gộp và lọc theo label
  3. Người dùng tạo alert rule (ngưỡng, kéo dài bao lâu) và nhận thông báo qua Slack, PagerDuty, email
  4. Dữ liệu cũ được giữ ở độ phân giải thấp hơn để xem xu hướng nhiều tháng

Yêu cầu phi chức năng

  • Ở quy mô lớn ghi được 50 triệu sample/s, không mất dữ liệu khi một node chết
  • Dữ liệu mới hiện trên dashboard trong < 30 s
  • Alert phát hiện sự cố trong < 1 phút sau khi metric vượt ngưỡng
  • Truy vấn 1 giờ gần nhất p95 < 1 s, 30 ngày p95 < 5 s
  • Sẵn sàng 99,9% và vẫn chạy khi hệ thống đang được giám sát sập

Ngoài phạm vi: Logs và distributed tracing; Phát hiện bất thường bằng machine learning; Tính tiền theo lượng dùng cho khách SaaS; Giám sát người dùng thật trên trình duyệt (RUM).

Thực thể và API

Series
Một chuỗi thời gian duy nhất = tên metric + bộ label, ví dụ http_requests_total{service="cart", status="500"}. Có series_id.
Sample
Một điểm đo: (series_id, timestamp ms, giá trị float64).
Block / Chunk
Gói sample của nhiều series trong 2 h, đã nén, kèm inverted index label → series. Bất biến sau khi ghi.
AlertRule
Biểu thức truy vấn, ngưỡng, thời gian 'for', mức độ, kênh nhận, label để gom nhóm.
Alert
Trạng thái của một rule cho một bộ label: inactive/pending/firing/resolved, thời điểm bắt đầu.
Tenant
Một khách hàng/đội: API key, hạn mức số series đang hoạt động, thời gian giữ dữ liệu.
MethodEndpointGhi chú
GET/metricsEndpoint trên mỗi service ở kiểu pull, trả text format các metric hiện tại.
POST/v1/writeAgent đẩy batch sample (protobuf + snappy). Trả 429 khi vượt hạn mức series.
GET/v1/query_range?q=&start=&end=&step=Truy vấn cho dashboard, step tự chọn độ phân giải phù hợp.
POST/v1/alert-rulesTạo/sửa rule. Lưu ở DB cấu hình, evaluator tải lại định kỳ.
POST/v1/silencesTạm tắt thông báo theo bộ label trong lúc bảo trì.

Thiết kế tổng thể

Đi lần lượt từng yêu cầu chức năng. Mục tiêu ở đây là chạy đúng, việc tối ưu để dành cho phần đào sâu.

1) Thu thập metrics từ server và ứng dụng

Ứng dụng giữ bộ đếm trong RAM và chỉ lộ ra con số hiện tại, rất rẻ. Phía thu thập gom lại theo chu kỳ 10-15 s, ghi theo batch chứ không ghi từng điểm.

  1. 1Ứng dụng dùng thư viện (prom-client) tăng counter, ghi histogram trong RAM
  2. 2Scraper/agent đọc /metrics mỗi 15 s, gắn label host, service, env
  3. 3Ingest kiểm tra tenant và hạn mức series, đẩy batch vào hàng đợi (quy mô lớn)
  4. 4Ingester ghi WAL rồi thêm sample vào head chunk trong RAM, mỗi 2 h đóng block nén xuống đĩa/S3

Nguồn: Grokking Modern System Design · ch.13 Distributed Monitoring ↗ · bài "Prerequisites of a Monitoring System", mục "Populate the metrics" (push hay pull) và "Application metrics" ; Grokking Modern System Design · ch.14 Monitor Server-side Errors ↗ · bài "Detailed Design of a Monitoring System", mục "Data collector" và "Service discoverer" ; Redis in Action ↗ · ch.5 · §5.2.1 Storing counters in Redis (tr. 94–98) ; Chưa có nguồn trong thư viện: cấu trúc chi tiết của TSDB (head block, WAL, nén delta-of-delta/XOR, compaction, downsample) không có trong sách của thư viện; chỉ nêu khái niệm cơ bản, không đưa số liệu sản phẩm

2) Truy vấn và vẽ dashboard

Dữ liệu vài giờ gần nhất nằm trong RAM của ingester, dữ liệu cũ nằm trong block trên object storage. Lớp truy vấn hỏi cả hai rồi ghép lại.

  1. 1Grafana gửi PromQL kèm khoảng thời gian và step
  2. 2Query frontend chia truy vấn dài theo ngày, trả phần đã có trong cache
  3. 3Querier tra inverted index để tìm series khớp label, đọc phần mới từ ingester và phần cũ từ store gateway
  4. 4Gộp, bỏ bản trùng từ các replica, tính hàm (rate, sum by) rồi trả về

Nguồn: Grokking Modern System Design · ch.14 Monitor Server-side Errors ↗ · bài "Detailed Design of a Monitoring System", mục "Querying service" và "Dashboard" ; Grokking Modern System Design · ch.14 ↗ · bài "Visualize Data in a Monitoring System", mục "Using heat maps to troubleshoot" ; Chưa có nguồn trong thư viện: cấu trúc chi tiết của TSDB (head block, WAL, nén delta-of-delta/XOR, compaction, downsample) không có trong sách của thư viện; chỉ nêu khái niệm cơ bản, không đưa số liệu sản phẩm

3) Tạo alert rule và nhận thông báo

Alert là một truy vấn chạy lặp lại. Phần đánh giá và phần gửi thông báo được tách riêng để gom nhóm, chống trùng và tắt tạm khi bảo trì.

  1. 1Rule evaluator chạy mỗi rule theo chu kỳ 30-60 s trên dữ liệu mới
  2. 2Vượt ngưỡng thì chuyển pending, giữ đủ thời gian 'for' (ví dụ 5 phút) thì firing
  3. 3Notifier gom alert theo label (cùng cluster, cùng service), bỏ trùng, áp silence
  4. 4Gửi Slack/PagerDuty, gửi thêm 'resolved' khi hết

Nguồn: Grokking Modern System Design · ch.14 Monitor Server-side Errors ↗ · bài "Detailed Design of a Monitoring System", mục "Alert Manager" (gửi alert khi vi phạm rule trong rules database) ; Grokking Modern System Design · ch.13 Distributed Monitoring ↗ · bài "Prerequisites of a Monitoring System", mục "Alerting"

4) Giữ dữ liệu cũ ở độ phân giải thấp

Không ai cần xem CPU từng 15 s của năm ngoái. Compactor gộp block nhỏ thành lớn và tạo bản tóm tắt 5 phút, 1 giờ để truy vấn dài vừa nhanh vừa rẻ.

  1. 1Compactor gộp các block 2 h thành block lớn hơn, dọn series đã xóa
  2. 2Tạo bản downsample 5 phút và 1 giờ, mỗi điểm lưu min, max, sum, count
  3. 3Xóa dữ liệu raw quá 15 ngày, bản 5 phút quá 90 ngày, bản 1 giờ quá 13 tháng
  4. 4Querier tự chọn độ phân giải theo step của truy vấn

Nguồn: Grokking Modern System Design · ch.14 Monitor Server-side Errors ↗ · bài "Detailed Design of a Monitoring System", mục "Storage" (TSDB lưu cục bộ, dữ liệu dư đẩy sang blob storage) ; Chưa có nguồn trong thư viện: cấu trúc chi tiết của TSDB (head block, WAL, nén delta-of-delta/XOR, compaction, downsample) không có trong sách của thư viện; chỉ nêu khái niệm cơ bản, không đưa số liệu sản phẩm

Hạ tầng theo quy mô

Cùng một sản phẩm, ba mức người dùng. Chọn từng mức để xem sơ đồ, phép tính và cấu hình server thay đổi thế nào.

Người dùng
1 công ty nhỏ · 200 server/container · 100.000 series
Tải đỉnh
≈ 6.667 sample/s
Chi phí ước lượng
≈ 100–300 USD/tháng (ước lượng)

Ở quy mô này người dùng là số target được giám sát, không phải DAU. Một cặp Prometheus cùng scrape, Grafana và Alertmanager là đủ, toàn bộ dữ liệu nằm trên đĩa local.

Dịch vụServer /metricsDatabasePrometheus ×2Dịch vụAlertmanagerDịch vụGrafanaBên ngoàiSlack/PagerDutyNgười dùngKỹ sư on-callscrape 15 salert firingthông báoPromQLdashboard

Ước lượng

  • Series: 200 target × 500 series = 100.000 series đang hoạt động
  • Ghi: 100.000 / 15 s (chu kỳ scrape) ≈ 6.667 sample/s
  • Đĩa: 6.667 × 86.400 ≈ 576 triệu sample/ngày × 1,5 B (nén) ≈ 864 MB/ngày × 15 ngày ≈ 13 GB
  • RAM head: 100.000 series × ~4 KB ≈ 400 MB, máy 16 GB còn dư cho truy vấn
  • Alert: 200 rule / 30 s ≈ 7 lần đánh giá/s

Nguồn con số: Grokking Modern System Design · ch.5 Back-of-the-envelope Calculations ↗ · bài "Examples of Resource Estimation", mục "Number of servers required", "Storage requirements" ; Quy tắc ước lượng của khoá học · /hoc/chon-server (500 req/s mỗi vCPU, chạy 60% CPU; số series mỗi target, chu kỳ scrape, byte mỗi sample sau nén và RAM mỗi series là giả định của khoá học, cần đo thực tế)

Bảng hạ tầng

Thành phầnChọnSố lượngCấu hìnhVì sao
PrometheusPrometheus (pull), cặp HA cùng scrape24 vCPU · 16 GB · 100 GB SSD6.667 sample/s và 13 GB là rất nhỏ; chạy 2 bản giống nhau để một cái chết vẫn còn dữ liệu và alert.
Exporternode_exporter + prom-client trong app1 mỗi hostVài chục MB RAMLộ /metrics cho CPU, RAM, đĩa và số đo của ứng dụng.
AlertmanagerAlertmanager cluster (gossip)21 vCPU · 1 GBHai Prometheus cùng bắn alert, Alertmanager bỏ trùng và gom nhóm.
DashboardGrafana11 vCPU · 2 GBVài chục người xem; mất Grafana không ảnh hưởng thu thập hay alert.
Thông báoSlack / PagerDuty / email1Dịch vụ bên ngoàiOn-call nhận cảnh báo ngoài hệ thống của mình.
Thay đổi so với mức trước
  • Điểm xuất phát: Prometheus pull, lưu local 15 ngày
  • HA bằng cách chạy 2 bản scrape giống nhau, không cần đồng bộ
  • Alertmanager tách khỏi Prometheus để bỏ trùng và gom nhóm
Nút thắt tiếp theo

Khi số series lên hàng triệu, RAM của một Prometheus không đủ, và đội muốn xem xu hướng nhiều tháng trong khi đĩa local chỉ giữ 15 ngày.

Cấu hình và chi phí là ước lượng để học và trình bày trong phỏng vấn. Xem cách tính ở bài Chọn server.

So sánh nhanh ba mức

Thành phầnMức 1Mức 2Mức 3
Người dùng1 công ty nhỏ · 200 server/container · 100.000 seriesCông ty 1 triệu DAU · 2.000 target · 2 triệu seriesSaaS kiểu Datadog · 1 triệu host · 500 triệu series
Chi phí≈ 100–300 USD/tháng (ước lượng)≈ 2.000–5.000 USD/tháng (ước lượng)≈ 150.000–400.000 USD/tháng (ước lượng, phần lớn là RAM ingester và băng thông giữa AZ)
Prometheus
Prometheus (pull), cặp HA cùng scrape
2 × 4 vCPU · 16 GB · 100 GB SSD
Prometheus + Thanos sidecar
2 shard × 2 replica = 4 × 4 vCPU · 16 GB · 200 GB SSD
Chưa cần
Exporter
node_exporter + prom-client trong app
1 mỗi host × Vài chục MB RAM
Chưa cầnChưa cần
Alertmanager
Alertmanager cluster (gossip)
2 × 1 vCPU · 1 GB
Alertmanager cluster
3 × 1 vCPU · 2 GB
Chưa cần
Dashboard
Grafana
1 × 1 vCPU · 2 GB
Grafana + Postgres lưu cấu hình
2 × 2 vCPU · 4 GB
Chưa cần
Thông báo
Slack / PagerDuty / email
1 × Dịch vụ bên ngoài
Chưa cầnChưa cần
Object storageChưa cần
S3 / GCS
1 × ≈ 2,2 TB (518 GB raw + 1,7 TB downsample)
S3 (block 2 h)
1 × ≈ 239 TB
Truy vấnChưa cần
Thanos Query + Query Frontend
2 × 4 vCPU · 8 GB
Query frontend + querier + Memcached
30–60 querier (HPA) × 8 vCPU · 16 GB mỗi pod
Store gatewayChưa cần
Thanos Store
2 × 4 vCPU · 16 GB
Đọc block trên S3, shard theo block
24 pod × 8 vCPU · 32 GB mỗi pod
CompactorChưa cần
Thanos Compactor
1 (chỉ được chạy 1 bản) × 4 vCPU · 16 GB · 500 GB đĩa tạm
Compactor + downsampler chia theo tenant
16 pod × 8 vCPU · 32 GB · 1 TB đĩa tạm
Ingest gatewayChưa cầnChưa cần
Go service trên Kubernetes, 3 AZ
167 pod (HPA) × 2 vCPU · 4 GB mỗi pod
KafkaChưa cầnChưa cần
MSK / Confluent, 3 AZ
20 broker × Topic samples 54 partition, RF 3, giữ 6 h
Ingester (TSDB)Chưa cầnChưa cần
TSDB kiểu Mimir / M3
81 node (27 mỗi AZ) × 16 vCPU · 128 GB · 1 TB NVMe
Rule evaluatorChưa cầnChưa cần
Evaluator shard theo tenant, có lease
84 pod × 4 vCPU · 8 GB mỗi pod
NotifierChưa cầnChưa cần
Cụm kiểu Alertmanager, đa tenant
6 pod × 2 vCPU · 4 GB mỗi pod

Đào sâu

Mỗi câu hỏi đi từ cách đơn giản đến cách người phỏng vấn mong đợi ở cấp senior.

1) Thu thập metrics nên theo kiểu pull hay push?

Cách chưa tốt
Mỗi service INSERT từng điểm đo vào một DB trung tâm

Mỗi lần đo là một lần ghi qua mạng. Hàng nghìn service ghi cùng lúc làm DB quá tải, và DB chậm thì kéo chậm luôn ứng dụng.

Đánh đổi: Dễ code nhất nhưng giám sát lại trở thành nguyên nhân gây sự cố.

Nguồn: Grokking Modern System Design · ch.13 Distributed Monitoring ↗ · bài "Prerequisites of a Monitoring System", mục "Populate the metrics" (mỗi lần đẩy dữ liệu là một request tới hệ giám sát; push dễ làm ngập hạ tầng, xem ch.14 mục "Improving our design")

Cách tốt
Pull: Prometheus scrape /metrics mỗi 15 s qua service discovery

Ứng dụng chỉ giữ số trong RAM, hệ thống giám sát tự quyết định lúc nào lấy nên không bị target gửi quá nhiều làm ngập (Grokking ch.13). Scrape lỗi thì biết ngay target chết (up = 0).

Đánh đổi: Phải đi được vào mạng của target (khó qua firewall, NAT, nhiều cloud); job chạy ngắn có thể kết thúc trước lần scrape; một scraper có giới hạn số target.

Nguồn: Grokking Modern System Design · ch.13 Distributed Monitoring ↗ · bài "Prerequisites of a Monitoring System", mục "Populate the metrics" (pull: server chỉ giữ metric, hệ giám sát lấy theo lịch của mình ; push có lợi khi có firewall) ; Grokking Modern System Design · ch.14 Monitor Server-side Errors ↗ · bài "Detailed Design of a Monitoring System", mục "Data collector" (chọn pull để tránh nghẽn mạng)

Cách tốt nhất
Agent tại chỗ: pull trong máy, push batch nén ra ngoài, có buffer

Agent (OpenTelemetry Collector, Prometheus agent, Datadog agent) scrape các target trong cùng máy hoặc cluster, gom batch, nén, đệm trên đĩa rồi đẩy ra qua HTTPS nên đi qua firewall dễ dàng. Gateway kiểm tra hạn mức rồi ghi vào Kafka để hấp thụ đột biến. Mỗi agent gửi heartbeat để phân biệt 'không có dữ liệu' với 'host chết'.

Đánh đổi: Phải vận hành agent trên mọi máy và phát hiện host chết dựa vào alert khi thiếu heartbeat thay vì scrape lỗi.

Nguồn: Grokking Modern System Design · ch.14 Monitor Server-side Errors ↗ · bài "Detailed Design of a Monitoring System", mục "Improving our design" (kết hợp pull trong data center với push từ secondary lên primary, hệ phân cấp để scale) ; Chưa có nguồn trong thư viện: agent cụ thể (OpenTelemetry Collector, Datadog agent), đệm đĩa và heartbeat không có trong sách

2) Lưu hàng trăm triệu series thế nào cho rẻ, truy vấn nhanh, và giữ được nhiều tháng?

Cách chưa tốt
Bảng Postgres (metric, labels JSONB, ts, value), một dòng mỗi sample

Mỗi dòng kèm index tốn 50-100 B. 50 triệu sample/s là hơn 4 nghìn tỷ dòng mỗi ngày, không DB quan hệ nào ghi hay lưu nổi.

Đánh đổi: Chỉ ổn cho vài nghìn series trong một dự án nhỏ.

Nguồn: Grokking Modern System Design · ch.13 Distributed Monitoring ↗ · bài "Prerequisites of a Monitoring System", mục "Persist the data" (cần time-series database cho lượng dữ liệu lớn) ; Grokking Modern System Design · ch.9 Databases ↗ · bài "Types of Databases", mục "Relational databases" › "Drawback"

Cách tốt
TSDB một máy (Prometheus, InfluxDB), giữ raw 15 ngày

Sample của một series nằm liền nhau. Timestamp nén bằng delta-of-delta, giá trị nén bằng XOR với giá trị trước (kiểu Gorilla) nên còn khoảng 1,3-2 B/sample. Inverted index label → series cho phép lọc nhanh.

Đánh đổi: Giới hạn ở RAM và đĩa của một máy; không có lịch sử dài; máy chết thì mất khoảng trống dữ liệu.

Nguồn: Grokking Modern System Design · ch.14 Monitor Server-side Errors ↗ · bài "Detailed Design of a Monitoring System", mục "Storage" (TSDB lưu cục bộ trên server giám sát) ; Chưa có nguồn trong thư viện: cấu trúc chi tiết của TSDB (head block, WAL, nén delta-of-delta/XOR, compaction, downsample) không có trong sách của thư viện; chỉ nêu khái niệm cơ bản, không đưa số liệu sản phẩm

Cách tốt nhất
Head trong RAM + WAL, block 2 h bất biến trên object storage, compactor downsample 5 phút / 1 giờ

Ingester giữ 2 h gần nhất trong RAM (có WAL, nhân bản 3 AZ) rồi đẩy block nén lên S3 một lần. Store gateway đọc block cũ theo yêu cầu, chỉ cache index. Compactor tạo bản 5 phút và 1 giờ, lưu min/max/sum/count nên avg và max vẫn đúng. Percentile không gộp được từ p99 nên phải lưu histogram bucket. Giữ raw 15 ngày, 5 phút 90 ngày, 1 giờ 13 tháng.

Đánh đổi: Nhiều thành phần phải vận hành; bản downsample làm mất đỉnh ngắn hơn step, trừ khi xem max.

Nguồn: Grokking Modern System Design · ch.14 Monitor Server-side Errors ↗ · bài "Detailed Design of a Monitoring System", mục "Storage" và "Improving our design" (blob storage cho dữ liệu dư, hệ phân cấp) ; Chưa có nguồn trong thư viện: cấu trúc chi tiết của TSDB (head block, WAL, nén delta-of-delta/XOR, compaction, downsample) không có trong sách của thư viện; chỉ nêu khái niệm cơ bản, không đưa số liệu sản phẩm

3) Đánh giá hàng triệu alert rule mỗi phút mà không bỏ sót và không spam?

Cách chưa tốt
Một cron chạy tuần tự tất cả rule

Một vòng chạy hết rule lâu hơn chu kỳ thì rule cuối luôn trễ. Máy cron chết là không còn alert nào, mà không ai biết.

Đánh đổi: Chỉ đủ cho vài trăm rule.

Nguồn: Grokking Modern System Design · ch.14 Monitor Server-side Errors ↗ · bài "Detailed Design of a Monitoring System", mục "Alert Manager" ; Chưa có nguồn trong thư viện: giới hạn của một cron tuần tự là suy luận của khoá, không có trong sách

Cách tốt
Chia rule cho nhiều evaluator theo hash(rule_id), chạy song song

Mỗi evaluator lo một phần rule, giữ trạng thái pending/firing. Alertmanager phía sau bỏ trùng và gom nhóm.

Đánh đổi: Mỗi rule là một truy vấn nên tải đọc tăng theo số rule; một evaluator chết thì phần rule của nó im lặng tới khi chia lại, đồng hồ 'for' bị reset.

Nguồn: Grokking Modern System Design · ch.14 Monitor Server-side Errors ↗ · bài "Detailed Design of a Monitoring System", mục "Alert Manager" ; Grokking Modern System Design · ch.9 Databases ↗ · bài "Data Partitioning", mục "Hash-based sharding"

Cách tốt nhất
Shard có lease, đọc dữ liệu nóng, recording rule, notifier gom nhóm, và giám sát chính hệ giám sát

Rule chia theo tenant/rule bằng consistent hashing kèm lease, trạng thái pending lưu ra ngoài nên failover không reset 'for'. Evaluator chỉ đọc vài phút gần nhất từ ingester. Biểu thức nặng dùng chung được tính trước bằng recording rule. Notifier gom nhóm, chặn alert con khi alert cha đã bắn (cluster sập thì không báo từng host), áp silence. Một alert 'watchdog' luôn bắn tới dịch vụ bên ngoài, ngừng nhận là biết hệ giám sát đã chết.

Đánh đổi: Phức tạp hơn nhiều; cần định nghĩa rõ thứ bậc alert để gom nhóm không che mất sự cố thật.

Nguồn: Grokking Modern System Design · ch.9 Databases ↗ · bài "Data Partitioning", mục "Consistent hashing" ; Grokking Modern System Design · ch.14 Monitor Server-side Errors ↗ · bài "Detailed Design of a Monitoring System", mục "Alert Manager" ; Building Microservices ↗ (.zip) · ch.8 Monitoring · mục "Synthetic Monitoring" (tr. 161) ; Chưa có nguồn trong thư viện: lease, recording rule, inhibition và alert watchdog không có trong sách

4) Một dev thêm label user_id vào metric và cả hệ thống chậm hẳn. Kiểm soát high-cardinality thế nào?

Cách chưa tốt
Cho phép mọi label, tin dev tự biết

Số series là tích số giá trị của các label: 1 triệu user × 20 endpoint × 5 status = 100 triệu series mới. Mỗi series tốn vài KB RAM trong ingester nên ingester hết RAM và sập.

Đánh đổi: Một commit làm sập giám sát của mọi đội.

⚠ Chưa có nguồn trong thư viện: khái niệm cardinality của label metric không có trong sách của thư viện; phép nhân số giá trị các label là phép tính thuần túy, RAM mỗi series là giả định của khoá

Cách tốt
Hạn mức ở gateway: tối đa series mỗi tenant và mỗi metric, chặn label bị cấm

Vượt hạn mức thì trả 429 hoặc bỏ sample, có dashboard cho từng đội thấy mình đang dùng bao nhiêu series. Danh sách label cấm (user_id, request_id, email) chặn từ cửa.

Đánh đổi: Dữ liệu bị bỏ âm thầm nếu không báo rõ, dev khó hiểu vì sao biểu đồ trống.

Nguồn: Microservices Patterns ↗ (.zip) · ch.11 · §11.3.4 Applying the Application metrics pattern ; Grokking Modern System Design · ch.13 Distributed Monitoring ↗ · bài "Prerequisites of a Monitoring System", mục "Metrics" ; Grokking Modern System Design · ch.19 Rate Limiter ↗ · bài "System Design: The Rate Limiter", mục "Why do we need a rate limiter?" (hạn mức, trả 429)

Cách tốt nhất
Đo cardinality theo metric, gộp ở agent, chuyển dữ liệu chi tiết sang logs/traces

Gateway ước lượng số series mới theo metric bằng HyperLogLog và cảnh báo trước khi chạm hạn mức. Agent áp relabel để gộp bớt label (bỏ pod_id sau khi sum). Cần đi từ một điểm trên biểu đồ tới request cụ thể thì gắn exemplar trỏ tới trace_id thay vì thêm label. Câu hỏi theo từng user đưa sang kho cột như ClickHouse.

Đánh đổi: Phải dạy đội dev cách đặt label và duy trì công cụ xem cardinality; một số câu hỏi chi tiết phải trả lời ở hệ khác.

Nguồn: Microservices Patterns ↗ (.zip) · ch.11 · §11.3.2 Applying the Log aggregation pattern và §11.3.3 Using the Distributed tracing pattern (chi tiết theo request nằm ở logs/traces) ; Grokking Modern System Design · ch.22 Distributed Logging ↗ · bài "Introduction to Distributed Logging", mục "Use sampling" ; Chưa có nguồn trong thư viện: HyperLogLog, exemplar, ClickHouse không có trong sách

Trong phỏng vấn

Nên nói
  • “Tôi tính tải theo số series và chu kỳ thu thập: sample/s = series / chu kỳ, RAM ingester tỉ lệ với số series.”
  • “Pull hợp với hạ tầng nội bộ; ở quy mô SaaS tôi dùng agent pull tại chỗ rồi push batch nén ra ngoài.”
  • “Dữ liệu chia theo tuổi: 2 h trong RAM, block nén trên S3, downsample lưu min/max/sum/count cho dữ liệu cũ.”
  • “Hệ giám sát cũng phải được giám sát: có alert watchdog luôn bắn ra dịch vụ bên ngoài.”
Bẫy hay gặp
  • Lưu mỗi sample một dòng trong DB quan hệ.
  • Không giới hạn cardinality, để label user_id hay request_id lọt vào metric.
  • Downsample bằng cách lấy trung bình rồi bỏ min/max, hoặc gộp percentile như số thường.
  • Đặt hệ giám sát chung hạ tầng với hệ bị giám sát, sập cùng nhau.

Đọc thêm

Time-series database
Time-series database lưu dữ liệu đo theo thời gian như metric server, giá cổ phiếu, vị trí GPS. Bài này giải thích đặc điểm của loại dữ liệu này, cách nén, downsampling và retention, so sánh TimescaleDB, InfluxDB, Prometheus, ClickHouse, và bẫy cardinality.
Scale ghi
Khi lượng ghi vượt sức một database: gom batch, ghi bất đồng bộ qua hàng đợi, chọn storage tối ưu cho ghi, chia dữ liệu theo key, xử lý partition nóng bằng salting, sharded counter và tổng hợp trước. Kèm con số cho từng quy mô.
Kafka
Kafka là log phân tán bền vững: topic chia partition, consumer đọc theo offset và có thể đọc lại. Bài này đi qua thứ tự theo partition, consumer group, replication với ISR và acks, giới hạn của exactly-once, cách chọn số partition và so sánh với SQS, Pub/Sub, RabbitMQ.
Sharding
Chia dữ liệu theo chiều dọc và chiều ngang, cách chọn shard key và tránh hotspot, so sánh range, hash và directory, resharding, query và transaction xuyên shard, khi nào chưa nên shard và một Postgres chịu được tới đâu.
Consistent hashing
Consistent hashing chia key cho nhiều node sao cho khi thêm hoặc bớt node chỉ một phần nhỏ key phải di chuyển. Bài này đi từ vấn đề của hash modulo tới hash ring, virtual node, replication dọc vòng và một cài đặt TypeScript ngắn.
Cấu trúc dữ liệu cho big data
Bloom filter, Count-Min Sketch, HyperLogLog, Top-K bằng heap và Space-Saving, skip list, LSM tree: mỗi cấu trúc giải bài toán gì, tốn bao nhiêu RAM, sai số bao nhiêu, và bài thiết kế nào cần tới.

Sách trong thư viện của bạn. Số chương có thể lệch nhẹ so với bản PDF.