Bài 2

Data Source & Query

Data source là gốc của mọi thứ trong Grafana — không có nó thì panel chẳng vẽ được gì. Bài này đi sâu cách Grafana kết nối tới các nguồn, các loại data source phổ biến, và cách viết truy vấn cho ba "ngôn ngữ" quan trọng nhất: PromQL (Prometheus), SQL (MySQL/PostgreSQL) và LogQL (Loki), cùng những khái niệm query dùng chung như time range, $__interval và Explore mode.

Data source là gì & cách Grafana kết nối
URL, xác thực, access mode

Một data source là cấu hình kết nối tới một hệ thống chứa dữ liệu. Bạn khai báo nó một lần (trong Connections → Data sources hoặc qua provisioning), rồi mọi panel chỉ việc chọn data source đó và viết query. Mỗi loại data source có một query editor riêng phù hợp với ngôn ngữ truy vấn của nó.

Khi khai báo, ba thông tin quan trọng nhất là:

Access modeCách hoạt độngKhi nào dùng
Server (proxy)Trình duyệt gọi Grafana server, server đứng ra gọi data source rồi trả kết quả về. Đây là mặc định và khuyến nghị.Gần như mọi trường hợp. Data source không cần lộ ra Internet, credential nằm an toàn ở server, tránh lỗi CORS.
Browser (direct)Trình duyệt của người dùng gọi thẳng tới data source, bỏ qua Grafana server.Rất hiếm — chỉ khi nguồn phải nằm cùng mạng với client và không đi qua được server. Dễ dính CORS và lộ endpoint.

Danh sách các data source phổ biến và chúng lưu loại dữ liệu gì:

Data sourceLoại dữ liệuNgôn ngữ truy vấn
PrometheusMetrics (time series) — thu thập theo kiểu pull, giám sát hạ tầng & app.PromQL
LokiLogs — log tập trung, gắn label giống Prometheus.LogQL
MySQLDữ liệu quan hệ / nghiệp vụ trong bảng SQL.SQL
PostgreSQLDữ liệu quan hệ / nghiệp vụ; hay kèm TimescaleDB cho time series.SQL
ElasticsearchLogs & document full-text; phân tích theo bucket thời gian.Lucene / Query DSL
InfluxDBMetrics time series (IoT, sensor, ứng dụng).Flux / InfluxQL
CloudWatchMetrics & logs của hạ tầng AWS (EC2, RDS, Lambda...).Metric query / Logs Insights
TempoTraces phân tán — theo vết một request xuyên nhiều service.TraceQL
Ghi nhớ: Grafana không copy dữ liệu về. Mỗi khi render panel nó gọi trực tiếp data source với time range hiện tại. Vì thế đặt URL, quyền đọc và access mode đúng ngay từ đầu là việc quan trọng nhất.
Prometheus & PromQL
Ngôn ngữ truy vấn metrics phổ biến nhất

Prometheus lưu metrics dưới dạng chuỗi time series, mỗi chuỗi được định danh bởi tên metric + tập label, ví dụ http_requests_total{method="GET", handler="/api"}. PromQL là ngôn ngữ để lọc, tổng hợp và tính toán trên các chuỗi này.

Có hai kiểu truy vấn:

Kiểu queryTrả vềDùng cho
Instant queryMột giá trị duy nhất tại một thời điểm (mới nhất).Panel Stat, Gauge, Table — chỉ cần con số hiện tại.
Range queryChuỗi giá trị theo nhiều mốc thời gian trong time range.Panel Time series (đồ thị đường) — vẽ diễn biến theo thời gian.

Bốn loại metric cần phân biệt vì cách query khác nhau:

LoạiĐặc điểmVí dụ
CounterChỉ tăng (reset về 0 khi restart). Không đọc trực tiếp — phải bọc rate().http_requests_total
GaugeLên xuống tự do, đọc trực tiếp là có nghĩa.node_memory_Available_bytes
HistogramChia quan sát vào các bucket (_bucket, _sum, _count). Dùng để tính quantile phía server.http_request_duration_seconds
SummaryGiống histogram nhưng quantile được tính sẵn tại client, không gộp được giữa nhiều instance.rpc_duration_seconds

Vì sao counter phải dùng rate()? Giá trị thô của counter (vd 1.240.512 request tích luỹ) gần như vô nghĩa và tụt về 0 mỗi lần service restart. Cái ta quan tâm là tốc độ tăng mỗi giây. Hàm rate() tính độ dốc trung bình trên một khoảng thời gian và tự xử lý các lần reset counter:

# request mỗi giây, trung bình trong 5 phút gần nhất
rate(http_requests_total[5m])

Các hàm PromQL then chốt:

HàmÝ nghĩa
rate(v[5m])Tốc độ tăng/giây trung bình của counter trên cửa sổ 5 phút. Mượt, phù hợp cho cảnh báo & đồ thị.
irate(v[5m])Tốc độ tức thời, chỉ lấy 2 điểm cuối. Nhạy với đột biến, tốt để soi spike nhưng gai góc.
sum by(label)(...)Cộng gộp các chuỗi, giữ lại nhóm theo label chỉ định (bỏ các label còn lại).
histogram_quantile(q, ...)Tính phân vị (vd p95, p99) từ các bucket của histogram.

Ví dụ thực tế — tổng request/s toàn hệ thống gom theo status code:

sum by(status_code) (rate(http_requests_total[5m]))

Ví dụ kinh điển — độ trễ p95 từ một histogram (chú ý phải sum ... by(le) trước khi tính quantile):

histogram_quantile(
  0.95,
  sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
)

Tỉ lệ lỗi (error rate) — phần trăm request trả về mã 5xx:

sum(rate(http_requests_total{status_code=~"5.."}[5m]))
  /
sum(rate(http_requests_total[5m]))
Mẹo về cửa sổ: khoảng trong [...] nên chứa ít nhất 4 lần scrape interval (thường là [5m] nếu scrape mỗi 15–30s). Cửa sổ quá nhỏ → đồ thị gãy khúc & nhiều lỗ; quá lớn → mượt nhưng chậm phản ứng với thay đổi.
SQL data source (MySQL / PostgreSQL)
Đọc thẳng dữ liệu nghiệp vụ

Với MySQL/PostgreSQL, bạn viết SQL bình thường nhưng để vẽ được time series, Grafana yêu cầu kết quả có một số hình dạng nhất định. Quan trọng nhất: phải có một cột thời gian (được đặt bí danh là time) cùng một hoặc nhiều cột giá trị số.

Grafana cung cấp các macro được thay thế lúc chạy theo time range & độ phân giải hiện tại của dashboard:

MacroThay bằngCông dụng
$__timeFilter(col)col BETWEEN '...' AND '...'Lọc theo đúng time range đang xem — đặt trong mệnh đề WHERE.
$__timeGroup(col, '1m')Biểu thức gom nhóm theo bucket thời gianGộp các dòng vào từng khoảng (1m, 5m...) để vẽ time series mượt.
$__intervalvd 30s, 1mBước thời gian Grafana tự tính từ độ rộng panel & time range; dùng làm bucket size.
$__timeFrom() / $__timeTo()Mốc đầu / cuối của rangeKhi cần chèn trực tiếp biên thời gian.

Ví dụ query cho panel Time series (PostgreSQL) — số đơn hàng theo từng khoảng thời gian:

SELECT
  $__timeGroup(created_at, $__interval) AS "time",
  count(*) AS orders
FROM orders
WHERE $__timeFilter(created_at)
GROUP BY 1
ORDER BY 1;

Ví dụ query cho panel Table — không cần cột time, chỉ cần các cột muốn hiển thị dưới dạng bảng:

SELECT
  product_name  AS "Sản phẩm",
  count(*)      AS "Số đơn",
  sum(amount)   AS "Doanh thu"
FROM orders
WHERE $__timeFilter(created_at)
GROUP BY product_name
ORDER BY "Doanh thu" DESC
LIMIT 20;
Cẩn thận: quên $__timeFilter() nghĩa là query quét toàn bộ bảng bất kể time range — cực chậm và tạo tải nặng lên DB. Luôn để macro time filter trong WHERE cho mọi query đọc dữ liệu vận hành.
Loki & LogQL
Truy vấn log tập trung

Loki là hệ thống log của Grafana Labs, thiết kế theo tư duy Prometheus: nó không index toàn văn nội dung log, mà chỉ index label của log stream (như app, namespace, pod). Một truy vấn LogQL luôn bắt đầu bằng stream selector trong { }:

# chọn tất cả log của app "api"
{app="api"}

Sau selector, thêm các filter expression để lọc theo nội dung dòng log:

Toán tửÝ nghĩa
|= "error"Dòng có chứa chuỗi "error".
!= "debug"Dòng không chứa chuỗi "debug".
|~ "5\\d\\d"Khớp biểu thức chính quy (regex).
!~ "..."Không khớp regex.
# log của api có chứa "error", loại bỏ dòng health-check
{app="api"} |= "error" != "healthz"

LogQL cũng làm được metric query — biến log thành số để vẽ đồ thị. Ví dụ đếm tốc độ dòng log/giây:

# số dòng log mỗi giây của app api, cửa sổ 5 phút
rate({app="api"}[5m])
# tốc độ dòng log lỗi, gom theo pod
sum by(pod) (rate({app="api"} |= "error" [5m]))
Điểm hay: vì Loki dùng cùng mô hình label như Prometheus, bạn có thể đặt cùng một biến $app hoặc $namespace cho cả panel metrics lẫn panel logs, và tương quan chúng trên cùng một dashboard khi điều tra sự cố.
Khái niệm query dùng chung
Áp dụng cho mọi data source

Dù dùng nguồn nào, có một số khái niệm về truy vấn lặp lại ở khắp Grafana:

Khái niệmÝ nghĩa
Time rangeKhoảng thời gian đang xem, chọn ở góc trên phải (vd "Last 6 hours"). Được truyền vào mọi query của dashboard.
$__intervalBước thời gian Grafana tự tính = time range ÷ số pixel ngang của panel. Dùng làm cửa sổ rate() động hoặc bucket size, tránh vẽ quá nhiều/quá ít điểm.
$__rate_intervalBiến chuyên cho rate() của Prometheus: đảm bảo cửa sổ luôn ≥ 4× scrape interval, tránh đồ thị bị lỗ khi zoom sát.
refId (A/B/C)Một panel có thể chứa nhiều query, mỗi query mang một refId. Cho phép vẽ nhiều chuỗi từ nhiều nguồn hoặc kết hợp qua transform/expression.
Time series frameDạng dữ liệu "cột time + cột giá trị số" — Grafana hiểu là đồ thị theo thời gian.
Table frameDạng bảng phẳng nhiều cột bất kỳ, không nhất thiết có trục thời gian — dùng cho panel Table.

Nhiều query trên một panel: ví dụ panel latency có thể chứa ba query cùng lúc — refId A vẽ p50, refId B vẽ p95, refId C vẽ p99 — dùng chung một trục thời gian. Bạn cũng có thể để refId A lấy từ Prometheus và refId B lấy từ một nguồn khác rồi so sánh.

Time series frame vs table frame là phân biệt hay gây bối rối: cùng một query SQL, nếu bạn alias cột thời gian là time và group theo bucket → Grafana coi là time series và vẽ đường; nếu không có cột time → nó rơi về table frame. Chọn sai frame là lý do phổ biến khiến panel "không hiện gì" dù query chạy đúng.

Explore mode là chế độ truy vấn ad-hoc, tách khỏi dashboard: mở một khung query đơn, thử PromQL/LogQL/SQL, xem kết quả tức thì, rồi mới đưa vào panel. Đây là nơi lý tưởng để dò dữ liệu, gỡ query và nhảy qua lại giữa metrics ↔ logs khi điều tra sự cố.

Thực hành tốt: luôn dùng $__rate_interval thay vì hardcode [5m] trong panel Prometheus, và dùng $__interval cho $__timeGroup trong SQL. Nhờ vậy đồ thị tự thích nghi khi bạn zoom in/out mà không phải sửa query.
← Quay lại
Tổng quan Grafana