DataOps nâng cao 6 — Observability & Data Reliability Engineering

13 thg 7, 2026 2 lượt xem
#data-engineering
#dataops
#data-observability
#sre
#slo

Từ "quan sát được" tới "kỹ thuật độ tin cậy dữ liệu"

Bài DataOps 6 — Observability & Incident đã dựng khung khái niệm: 5 trụ, alerting, quy trình sự cố. Bài này đi xuống mức kỹ sư — cách đo từng trụ bằng số cụ thể, cách đặt SLO và error budget cho một data product, và cách vận hành sự cố dữ liệu như một đội on-call thực thụ.

Ý tưởng lớn: lấy nguyên bộ tư duy SRE (Site Reliability Engineering) — mà Observability tổng quan mô tả cho hệ thống phần mềm — và áp vào dữ liệu. Đó là Data Reliability Engineering (DRE): coi mỗi bảng/báo cáo quan trọng là một sản phẩm có mức dịch vụ cam kết, đo được, có ngân sách lỗi, có người trực và có quy trình khi hỏng. Ở ngân hàng, mục tiêu tối thượng của DRE rất cụ thể: tự phát hiện số liệu báo cáo sai trước khi Ban điều hành hoặc NHNN thấy.

Năm trụ và cách đo thực tế

Nhắc lại 5 trụ nhưng lần này gắn với cách tínhngưỡng dùng được ngay.

TrụCâu hỏiĐo thế nàoNgưỡng minh hoạ
FreshnessDữ liệu mới nhất tới khi nào?MAX(created_at) so với now; độ trễ (phút/giờ)trễ > 6h vào 7h sáng = P1
VolumeSố dòng hôm nay bình thường không?Đếm theo ngày, so baseline động (AVG cửa sổ trượt)lệch > 30% so trung bình 7 ngày
SchemaCột/kiểu có đổi không?So information_schema/manifest giữa hai lầnthêm/xoá/đổi kiểu cột = alert
Distribution/QualityGiá trị có "lạ" không?null rate, min/max, phân phối, driftnull rate của trường khoá tăng đột biến
LineageCái này ảnh hưởng những gì?Đồ thị phụ thuộc, tới mức cột→cộtdùng khi khoanh vùng tác động

Freshness — đo độ trễ, không chỉ "có dữ liệu"

Freshness không phải câu hỏi có/không mà là trễ bao nhiêu. Đo bằng MAX(created_at) của bảng rồi trừ thời điểm hiện tại. Với transactions, ta có thể tính ngay số ngày dữ liệu bị chậm:

-- ▶ Chạy được
SELECT
  MAX(created_at)                                      AS latest_txn,
  (CURRENT_DATE - MAX(created_at)::date)               AS days_behind,
  COUNT(*) FILTER (WHERE created_at::date = CURRENT_DATE) AS rows_today
FROM transactions;

Nếu days_behind > 0 vào giờ báo cáo nghĩa là batch đêm chưa đổ đủ. Ở mức DRE, freshness nên đo theo lát cắt (từng chi nhánh, từng nguồn) chứ không chỉ trên tổng — vì một chi nhánh trễ có thể bị "che" bởi 39 chi nhánh đúng hạn.

Volume — baseline động thay vì ngưỡng cứng

Đặt ngưỡng cứng ("phải > 10.000 dòng/ngày") sẽ sai vào cuối tuần/lễ. Cách đúng là baseline động: so số dòng hôm nay với trung bình trượt của N ngày gần nhất, rồi cờ (flag) khi lệch quá biên. Ta tính đếm theo ngày, dùng AVG(...) OVER cửa sổ 7 ngày trước đó, và ép ::numeric khi làm tròn:

-- ▶ Chạy được
WITH daily AS (
  SELECT created_at::date AS d, COUNT(*) AS n
  FROM transactions
  GROUP BY created_at::date
),
scored AS (
  SELECT
    d, n,
    AVG(n) OVER (ORDER BY d ROWS BETWEEN 7 PRECEDING AND 1 PRECEDING) AS baseline_7d
  FROM daily
)
SELECT
  d, n,
  ROUND(baseline_7d::numeric, 1)                                   AS baseline_7d,
  ROUND((100.0 * (n - baseline_7d) / NULLIF(baseline_7d, 0))::numeric, 1) AS pct_diff,
  CASE
    WHEN baseline_7d IS NULL THEN 'no-baseline'
    WHEN ABS(n - baseline_7d) > 0.30 * baseline_7d THEN 'ANOMALY'
    ELSE 'ok'
  END                                                              AS status
FROM scored
ORDER BY d DESC;

Cột pct_diff cho biết lệch bao nhiêu phần trăm so với nền; status = 'ANOMALY' chính là tín hiệu cần alert. Đây là "tự học baseline trên production" — điều khiến observability khác với test.

Distribution/Quality — null rate & drift

Chất lượng dữ liệu đo bằng các thống kê trên giá trị: tỷ lệ null, tỷ lệ rỗng, giá trị ngoài miền, và drift (phân phối hôm nay lệch so với hôm qua). Đơn giản nhất và hiệu quả nhất là null rate của các trường khoá — địa chỉ, mã ngành, phân loại nợ:

-- ▶ Chạy được
SELECT
  COUNT(*)                                                        AS total,
  COUNT(*) FILTER (WHERE city IS NULL OR btrim(city) = '')        AS missing_city,
  ROUND(
    100.0 * COUNT(*) FILTER (WHERE city IS NULL OR btrim(city) = '')
    / NULLIF(COUNT(*), 0)::numeric, 2
  )                                                               AS null_rate_pct
FROM customers;

Ở DRE, ta không chạy check này một lần mà ghi null_rate_pct theo ngày vào một bảng metric, rồi cảnh báo khi nó nhảy (ví dụ từ 2% lên 25%) — đó là drift báo hiệu nguồn đổi cách nhập liệu.

Schema & Lineage

Schema drift phát hiện bằng cách so information_schema.columns (hoặc manifest.json của dbt) giữa hai lần chạy: cột mới xuất hiện, cột biến mất, hay kiểu đổi (NUMERICTEXT) đều cần alert vì có thể làm gãy công thức phía dưới. Lineage không dùng để phát hiện lỗi mà để khoanh vùng tác động: khi fact_du_no sai, lineage cột→cột cho biết chính xác dashboard/báo cáo nào ăn theo trường đó để dán nhãn nghi ngờ. Chi tiết lineage xem Governance — Catalog & Lineage.

Observability ≠ Test

Đây là phân biệt cốt lõi (đã bàn ở bài quality-contracts, nhưng nhắc lại):

Test / ContractObservability
Bắt lỗibiết trước (assertion)chưa biết (bất thường)
Cơ chếquy tắc do người viếttự học baseline từ production
Khi nào chạytrong CI/pipeline, chặn cứngliên tục trên prod, cảnh báo
Ví dụ"balance ≥ 0", "id unique""volume tụt 40% so 7 ngày"

Cần cả hai: test chặn lỗi đã lường trước trước khi lên prod; observability bắt lỗi lạ mà không assertion nào ngờ tới.

Công cụ

  • Elementary — gắn thẳng vào dbt, sinh test freshness/volume/anomaly từ metadata, có report.
  • Monte Carlo / Soda — nền tảng observability độc lập, quét query log & metadata, ML anomaly detection.
  • Tự dựng — chạy các SQL như trên theo lịch, ghi vào bảng metric, alert khi lệch baseline. Với NCB, bắt đầu tự dựng vài check freshness/volume/null đã bao phủ phần lớn rủi ro trước khi mua công cụ.

SLI / SLO / SLA cho data product

Đây là trái tim của DRE. Ba khái niệm SRE, dịch sang dữ liệu:

  • SLI (Service Level Indicator)chỉ số đo được về chất lượng dịch vụ dữ liệu.
  • SLO (Service Level Objective)mục tiêu nội bộ cho SLI (ví dụ 99%).
  • SLA (Service Level Agreement)hợp đồng với người dùng, thường lỏng hơn SLO một chút, kèm hệ quả.

SLI cho data product thường gồm:

SLIĐịnh nghĩaCách đo
Freshness% ngày dữ liệu sẵn sàng trước giờ cắt(số ngày đúng hạn) / (tổng ngày)
Completeness% dòng/trường bắt buộc không null1 − null_rate của trường khoá
Accuracy% số liệu khớp nguồn đối chiếuso với reconciliation control totals
Availability% thời gian bảng/API truy vấn đượcuptime của endpoint phục vụ

Error budget = phần "được phép hỏng". SLO freshness 99,5%/tháng (≈ 30 ngày) cho phép ~3,6 giờ trễ trước giờ cắt trong tháng. Nếu tháng này đã "đốt" hết budget vì hai sự cố batch, đội đóng băng thay đổi rủi ro và ưu tiên gia cố độ tin cậy thay vì ra tính năng mới — đúng cơ chế error budget của SRE.

SLA với consumer là bản cam kết viết ra: "Báo cáo dư nợ sẵn sàng trước 7h30 mỗi ngày làm việc, đối soát khớp core banking, nếu sai đội dữ liệu thông báo trong 30 phút kể từ khi phát hiện." SLA nên rộng hơn SLO (SLO 7h00, SLA 7h30) để còn biên phản ứng.

Alerting thông minh — chống alert fatigue

Alert nhiều mà nhiễu thì đội sẽ tê liệt (alert fatigue) và bỏ qua cả cảnh báo thật. Nguyên tắc:

  • Severity rõ ràng. P1 = báo cáo gửi lãnh đạo/NHNN sắp sai → gọi điện on-call; P2 = lệch nhẹ, chờ được → Slack; P3 = ghi log, xem sau.
  • Gom & khử trùng lặp. Một nguồn hỏng làm 20 bảng downstream fail → một alert gốc kèm danh sách ảnh hưởng (nhờ lineage), không phải 20 tin.
  • Đúng người. Route theo ownership của data product, không spam cả kênh chung. Kèm ngữ cảnh: bảng nào, lệch bao nhiêu, so baseline nào, runbook ở đâu.
  • Actionable. Mỗi alert phải trả lời được "tôi cần làm gì bây giờ?". Alert không hành động được thì hạ severity hoặc bỏ.
  • Ngưỡng động, cửa sổ dập rung. Dùng baseline động (như query volume ở trên) và yêu cầu lệch giữ N chu kỳ mới bắn, tránh nhấp nháy.

Incident management cho dữ liệu

Quy trình sự cố dữ liệu, sáu bước:

  • Kiềm chế (containment) trước khắc phục. Ở ngân hàng, chặn số sai lan ra quan trọng hơn sửa nhanh: dừng job publish báo cáo, dán nhãn "đang kiểm tra" lên dashboard, để không ai ra quyết định trên số sai.
  • Runbook — tài liệu từng bước cho từng loại sự cố quen thuộc: "chi nhánh trễ dữ liệu", "schema nguồn đổi", "null tăng đột biến". Ai trực cũng làm được như nhau lúc 5h sáng.
  • On-call bền vững — xoay ca công bằng, có primary/secondary, giới hạn số lần bị đánh thức; nếu một alert kêu quá thường thì phải sửa gốc chứ không để nó bào mòn đội.
  • Blameless postmortem — mổ xẻ hệ thống và quy trình đã cho phép lỗi xảy ra, không đổ lỗi cá nhân. Kết quả là action item cụ thể (thêm check freshness theo chi nhánh, lập kênh báo đổi lịch với nguồn), không phải một cái tên bị nêu.

Truyền thông khi số liệu sai (data incident comms)

Cực kỳ quan trọng ở ngân hàng. Khi phát hiện báo cáo đã phát hành bị sai:

  1. Chủ động báo trước khi bị hỏi. Gửi "thông báo lỗi số liệu" nêu rõ: báo cáo/bảng nào sai, sai từ khi nào, ảnh hưởng quyết định gì, đang xử lý ra sao, khi nào có bản đúng.
  2. Dán nhãn nghi ngờ lên mọi dashboard/báo cáo ăn theo dữ liệu đó (dùng lineage để tìm hết).
  3. Phát hiện sớm là mục tiêu tối thượng — tự bắt lỗi trước khi lãnh đạo/NHNN thấy để còn giải trình chủ động thay vì bị chất vấn.

Văn hoá reliability & hai chỉ số cải tiến

DRE đo tiến bộ bằng hai chỉ số mượn từ SRE:

  • MTTD (Mean Time To Detect) — trung bình từ lúc lỗi xảy ra tới lúc đội biết. 5 trụ observability kéo MTTD xuống.
  • MTTR (Mean Time To Recover) — trung bình từ lúc biết tới lúc khắc phục xong. Runbook + on-call + backfill tự động kéo MTTR xuống.

Mỗi postmortem nên hỏi "lần này MTTD/MTTR bao nhiêu, làm gì để lần sau nhanh hơn?". Đó là vòng cải tiến liên tục biến độ tin cậy dữ liệu thành kỷ luật kỹ thuật, không phải may rủi.

Use case thực tế

Bối cảnh: Đội dữ liệu NCB coi bảng fact_du_no (dư nợ toàn hàng) là một data product phục vụ báo cáo gửi Ban điều hành mỗi 7h30 và một phần chỉ tiêu gửi NHNN. Trước đây chỉ có "job Airflow xanh", không có SLO, không có on-call dữ liệu.

Áp DRE (số minh hoạ):

  1. Định nghĩa data product + SLI/SLO. SLI: freshness (sẵn sàng trước 7h00), completeness (null rate trường phân loại nợ), accuracy (đối soát tổng dư nợ với core banking). SLO: 99,5% ngày làm việc/tháng cho mỗi SLI. SLA với Ban điều hành: sẵn sàng 7h30, báo lỗi ≤ 30 phút.
  2. Dựng 5 trụ. Freshness theo từng chi nhánh (query MAX(created_at) như trên, chạy theo lát cắt), volume theo ngày so baseline 7 ngày (đúng query volume ở trên), null rate trường khoá ghi vào bảng metric, schema drift so manifest, lineage từ fact_du_no xuống 6 dashboard.
  3. Alerting phân severity. Chi nhánh thiếu dữ liệu trước giờ cắt → P1 gọi on-call 5h sáng; lệch volume nhẹ → P2 Slack. Gom một nguồn hỏng thành một alert kèm danh sách 6 dashboard ảnh hưởng.
  4. Sự cố thật. Một sáng, tổng dư nợ giảm 12%, Airflow vẫn xanh — 3 chi nhánh đổi lịch batch, đẩy dữ liệu muộn. Freshness-theo-chi-nhánh bắn P1 lúc 5h. On-call theo runbook: dừng publish, dán nhãn dashboard, báo đội nguồn, chờ đủ dữ liệu, backfill rồi chạy lại. Báo cáo phát hành 7h20 — trễ 20 phút nhưng đúng sốtrong SLA.
  5. Blameless postmortem. Root cause: thiếu freshness theo chi nhánh + nguồn đổi lịch không báo. Action item: (đã có freshness theo chi nhánh), lập kênh thông báo đổi lịch với đội nguồn.

Kết quả sau một quý: phát hiện 6 sự cố dữ liệu trước khi người dùng thấy (trước đó: 0). MTTD từ "người dùng báo sau vài giờ" xuống < 15 phút; MTTR trung bình ~35 phút nhờ runbook + backfill. Không lần nào báo cáo NHNN phát hành với số sai. Error budget freshng còn 60% cuối quý — đủ để đội tiếp tục ship cải tiến. Cách gây dựng thói quen vận hành này trong đội xem thực hành đội nhóm.

Ghi nhớ

  • DRE = áp tư duy SRE cho dữ liệu: mỗi bảng quan trọng là data product có SLO, error budget, on-call, postmortem.
  • 5 trụ với cách đo cụ thể: freshness (độ trễ MAX(created_at)), volume (baseline động = AVG cửa sổ trượt), schema (so manifest/information_schema), distribution (null rate + drift), lineage (khoanh vùng tác động cột→cột).
  • Observability ≠ test: test bắt lỗi biết trước bằng assertion; observability bắt lỗi chưa biết bằng tự học baseline trên production. Cần cả hai.
  • SLI/SLO/SLA cho data product: SLI (freshness %, completeness %, accuracy, availability) → SLO nội bộ + error budget → SLA cam kết với consumer (lỏng hơn SLO).
  • Error budget hết → đóng băng thay đổi rủi ro, ưu tiên gia cố độ tin cậy.
  • Alerting thông minh: severity, gom & khử trùng lặp, đúng người theo ownership, actionable — chống alert fatigue.
  • Incident: phát hiện → phân loại → kiềm chế (dừng publish, dán nhãn) → khắc phục + backfill → RCA → blameless postmortem. Có runbookon-call bền vững.
  • Truyền thông khi số sai (quan trọng ở ngân hàng): chủ động báo lỗi, dán nhãn nghi ngờ, phát hiện trước khi lãnh đạo/NHNN thấy.
  • Đo cải tiến bằng MTTD & MTTR — kéo cả hai xuống là thước đo văn hoá reliability.

Bài viết liên quan

CSV/JSON/Parquet/Avro, lưu trữ theo hàng vs cột, nén, và OLTP vs OLAP.

13 thg 7, 2026 8

Data Engineering là gì, vai trò trong vòng đời dữ liệu, và bức tranh hệ sinh thái công cụ.

13 thg 7, 2026 5

Vì sao xử lý phân tán, mô hình Spark (RDD/DataFrame), lazy evaluation, shuffle và tối ưu.

13 thg 7, 2026 5

Đảm bảo chất lượng & minh bạch dữ liệu bằng dbt: data tests dựng sẵn (unique, not_null, relationships, accepted_values), singular test, unit test, và tài liệu tự sinh kèm lineage graph.

13 thg 7, 2026 5