DataOps 6 — Data Observability & xử lý sự cố
Khi pipeline "xanh" nhưng số liệu vẫn sai
Một buổi sáng thứ Hai, báo cáo dư nợ toàn hàng gửi Ban điều hành hiển thị tổng dư nợ giảm 12% so với hôm trước. Airflow báo tất cả job success, màu xanh hết. CPU, RAM, disk của cluster đều bình thường. Không có alert nào. Nhưng con số thì sai — hoá ra một hệ nguồn đêm qua đẩy thiếu một phân vùng chi nhánh, pipeline vẫn "chạy thành công" trên phần dữ liệu thiếu đó.
Đây là khoảng mù chết người của các đội dữ liệu: hạ tầng khoẻ không có nghĩa là dữ liệu đúng. Bài này thuộc series DataOps. Sau khi đã có testing & chất lượng chặn lỗi trước khi merge và CI/CD đưa mọi thứ lên prod an toàn, ta cần lớp cuối cùng: quan sát và ứng phó khi dữ liệu ở production đi sai — data observability và incident management.
Data observability khác gì monitoring hạ tầng?
Monitoring hạ tầng (xem tổng quan observability) quan sát hệ thống: CPU, bộ nhớ, độ trễ API, tỷ lệ lỗi HTTP, log ứng dụng. Nó trả lời câu hỏi "máy có khoẻ không?".
Data observability quan sát chính dữ liệu chảy qua hệ thống: dữ liệu có mới không, số bản ghi có hợp lý không, cấu trúc có đổi không, giá trị có lệch không. Nó trả lời câu hỏi "dữ liệu có đúng và tin được không?". Một pipeline có thể xanh 100% về hạ tầng mà vẫn tạo ra dữ liệu rác — đúng như tình huống mở bài.
| Monitoring hạ tầng | Data observability | |
|---|---|---|
| Đối tượng | Máy chủ, service, network | Bảng, cột, dòng dữ liệu |
| Ví dụ metric | CPU %, latency, error rate | Độ tươi, số bản ghi, tỷ lệ null |
| Câu hỏi | Hệ thống có sống không? | Dữ liệu có đúng không? |
| Trạng thái nguy hiểm | Server down | Job xanh nhưng số liệu sai |
Năm trụ của data observability
Data observability chuẩn hoá quanh 5 trụ (pillars) — 5 chiều cần liên tục quan sát trên mỗi bảng/dataset quan trọng.
- Freshness (độ tươi): dữ liệu có được cập nhật đúng hạn không? Bảng giao dịch đáng lẽ có bản ghi tới 6h sáng, mà bản ghi mới nhất là 2h chiều hôm qua → nguồn đã tắc. Đây là trụ hay bị vi phạm nhất và cũng dễ đo nhất: nhìn
MAX(created_at). - Volume (khối lượng): số bản ghi mỗi kỳ có hợp lý không? Mỗi ngày thường có ~500K giao dịch; hôm nay chỉ 40K (nguồn đẩy thiếu) hoặc 5 triệu (nhân bản/double-load) đều là dấu hiệu.
- Schema (cấu trúc): cột có bị thêm/xoá/đổi kiểu bất ngờ không? Hệ nguồn đổi
amounttừNUMERICsang chuỗi, hoặc bỏ cộtbranch_code, làm pipeline hạ nguồn hoặc gãy, hoặc tệ hơn — âm thầm tính sai. - Distribution (phân phối): giá trị trong cột có lệch khỏi bình thường không? Tỷ lệ null của
customer_idtừ 0.1% vọt lên 30%; phân phốikindgiao dịch đột nhiên toàn một loại;balancexuất hiện giá trị âm hàng loạt. - Lineage (truy vết dòng dữ liệu): khi một bảng nguồn hỏng, những báo cáo/model nào phía hạ nguồn bị ảnh hưởng? Lineage (xem catalog & lineage) không phát hiện lỗi mà giúp khoanh vùng tác động khi lỗi đã xảy ra — trả lời "phải báo ai, sửa cái gì".
Observability khác data testing thế nào?
Đây là điểm hay nhầm. Data testing (bài 4) và observability bổ trợ nhau chứ không thay thế:
| Data testing | Data observability | |
|---|---|---|
| Bản chất | Kỳ vọng biết trước | Phát hiện bất thường chưa biết |
| Cách hoạt động | Ta viết assertion rõ ràng: not_null, amount > 0 | Hệ tự học baseline rồi cảnh báo khi lệch |
| Thời điểm | Chủ yếu trong CI, trước deploy | Liên tục, trên production |
| Ví dụ | "Cột id không được null" | "Tỷ lệ null cột id hôm nay cao bất thường so với 30 ngày trước" |
| Điểm yếu | Chỉ bắt lỗi ta đã nghĩ ra | Có thể nhiễu, cần tinh chỉnh ngưỡng |
Nói ngắn gọn: testing bắt lỗi bạn lường trước; observability bắt lỗi bạn không lường trước bằng cách so hiện tại với hành vi lịch sử (baseline tự học). Một đội trưởng thành cần cả hai.
Công cụ: thị trường có Monte Carlo, Soda (Soda Core/Soda Cloud), Elementary (chuyên cho dbt, chạy ngay trong dự án dbt), hoặc tự dựng bằng các truy vấn metric + bảng lưu lịch sử + ngưỡng. Với đội nhỏ, tự dựng vài check freshness/volume/null bằng SQL đã bắt được phần lớn sự cố thực tế trước khi đầu tư công cụ trả phí.
Tự dựng observability bằng SQL
Không cần công cụ đắt tiền để bắt đầu. Ba check dưới đây chạy được ngay trên sandbox, minh hoạ đúng tinh thần 3 trụ đầu.
Freshness — bản ghi mới nhất theo tài khoản, tìm tài khoản "tắc": liệt kê tài khoản mà giao dịch gần nhất đã quá cũ (ở đây minh hoạ ngưỡng 30 ngày) — dấu hiệu luồng dữ liệu tài khoản đó ngừng chảy.
-- ▶ Chạy được
SELECT a.id AS account_id, a.account_no,
MAX(t.created_at) AS last_txn_at,
(CURRENT_DATE - MAX(t.created_at)::date) AS days_stale
FROM accounts a
JOIN transactions t ON t.account_id = a.id
GROUP BY a.id, a.account_no
HAVING (CURRENT_DATE - MAX(t.created_at)::date) > 30
ORDER BY days_stale DESC;
Volume theo ngày + phát hiện tăng/giảm vọt so với trung bình: đếm số giao dịch mỗi ngày, so với trung bình động 7 ngày trước đó; ngày nào lệch quá ±50% so với baseline là đáng ngờ (đẩy thiếu hoặc double-load).
-- ▶ Chạy được
WITH daily AS (
SELECT date_trunc('day', created_at)::date AS d,
COUNT(*) AS n
FROM transactions
GROUP BY 1
),
baseline AS (
SELECT d, n,
AVG(n) OVER (ORDER BY d ROWS BETWEEN 7 PRECEDING AND 1 PRECEDING) AS avg7
FROM daily
)
SELECT d, n,
ROUND(avg7::numeric, 1) AS avg_7d,
ROUND(((n - avg7) / NULLIF(avg7, 0) * 100)::numeric, 1) AS pct_vs_baseline
FROM baseline
WHERE avg7 IS NOT NULL
AND ABS((n - avg7) / NULLIF(avg7, 0)) > 0.5
ORDER BY d;
Trụ distribution cũng dựng bằng SQL tương tự: đếm tỷ lệ null hoặc tỷ lệ giá trị bất thường mỗi ngày (ví dụ COUNT(*) FILTER (WHERE customer_id IS NULL) / COUNT(*)) rồi so với baseline — nếu vọt lên là cảnh báo. Bảng metric lịch sử này chính là "baseline tự học" phiên bản thủ công.
Giám sát pipeline (không chỉ dữ liệu)
Song song với 5 trụ (đo dữ liệu), ta vẫn phải giám sát pipeline (đo quá trình):
- Job success/fail: job có chạy xong không, có bước nào lỗi không.
- Thời gian chạy (duration): job thường chạy 20 phút, hôm nay 90 phút → có thể sắp timeout hoặc nguồn chậm.
- Độ trễ (latency/lag): khoảng cách giữa thời điểm dữ liệu có sẵn ở nguồn và thời điểm nó xuất hiện ở bảng đích.
- SLA của pipeline: ví dụ "bảng dư nợ phải sẵn sàng trước 7h sáng"; trễ là vi phạm SLA, alert.
Giám sát pipeline bắt lỗi "quá trình gãy"; observability 5 trụ bắt lỗi "kết quả sai dù quá trình xanh". Cần cả hai.
Alerting đúng: chống mệt mỏi vì báo động
Observability tệ nhất là khi nó kêu quá nhiều. Alert nhiễu → người ta tắt thông báo → alert thật bị bỏ lỡ (alert fatigue). Nguyên tắc alerting tốt:
- Gắn severity: phân mức rõ ràng, ví dụ P1 (báo cáo NHNN sai/thiếu, ảnh hưởng khách hàng) → gọi điện on-call; P2 (một bảng nội bộ tắc, có thể chờ vài giờ) → Slack; P3 (lệch nhẹ, theo dõi) → chỉ ghi log/dashboard.
- Đúng người: alert bảng dư nợ đến đội quản trị rủi ro; alert pipeline hạ tầng đến đội platform. Sai người = alert vô nghĩa.
- Có ngữ cảnh: alert phải kèm bảng nào, lệch bao nhiêu so baseline, link runbook — không chỉ "job X failed".
- Chống nhiễu: gộp alert (dedupe), đặt ngưỡng hợp lý, snooze khi đang bảo trì, dùng "khoảng chờ" (một chỉ số phải lệch N lần liên tiếp mới báo).
Incident management cho dữ liệu
Khi có sự cố dữ liệu thật, cần một quy trình chuẩn thay vì mỗi người xử một kiểu. Vòng đời một data incident:
- Phát hiện: từ alert observability hoặc từ người dùng báo (lý tưởng là ta phát hiện trước người dùng).
- Phân loại (triage): gán severity — số liệu này có gửi NHNN/Ban điều hành không? Ảnh hưởng bao nhiêu consumer? Từ đó quyết mức khẩn.
- Ứng phó & kiềm chế (containment): ưu tiên ngăn lan rộng trước khi tìm nguyên nhân. Ba đòn kiềm chế điển hình:
- Dừng pipeline hạ nguồn để dữ liệu sai không lan vào báo cáo.
- Rollback bảng về bản tốt gần nhất (snapshot/partition trước đó).
- Thông báo consumer: dán nhãn "dữ liệu đang nghi ngờ, chưa dùng để ra quyết định" lên dashboard/báo cáo bị ảnh hưởng (dùng lineage để biết ai cần báo).
- Khắc phục (remediation): sửa gốc lỗi rồi backfill lại phần dữ liệu sai/thiếu, chạy lại pipeline, đối chiếu tổng.
- Root cause analysis: tìm nguyên nhân gốc, không dừng ở triệu chứng. "Số sai vì thiếu phân vùng" là triệu chứng; "vì hệ nguồn đổi lịch batch mà không thông báo, và ta không có check freshness theo chi nhánh" mới là gốc.
- Hậu kiểm (postmortem): viết lại toàn bộ, rút bài học, tạo action item.
Blameless postmortem & bài học
Postmortem không đổ lỗi (blameless): tập trung vào hệ thống và quy trình đã cho phép lỗi xảy ra, không vào cá nhân. Nếu ai gõ sai một câu và làm hỏng số liệu toàn hàng, câu hỏi đúng là "vì sao hệ thống cho phép một câu gõ tay tác động thẳng lên prod mà không có gate/check?", không phải "ai gõ?". Văn hoá đổ lỗi khiến người ta giấu sự cố → mất cơ hội học. Mỗi postmortem nên có: dòng thời gian, tác động (bao nhiêu báo cáo, bao lâu), root cause, và action item có người chịu trách nhiệm + deadline (ví dụ: "thêm check freshness theo chi nhánh vào observability trước cuối tháng").
On-call, runbook và SLA/SLO cho sản phẩm dữ liệu
On-call: phân ca trực để luôn có người phản hồi P1 ngoài giờ, có escalation (không phản hồi trong X phút → báo cấp trên). Đội dữ liệu ngân hàng chạy báo cáo đêm rất cần on-call vì lỗi hay lộ lúc batch đêm.
Runbook: tài liệu "khi sự cố X xảy ra thì làm 1-2-3". Ví dụ runbook "pipeline dư nợ trễ SLA": kiểm tra job nào tắc → xem log → nếu do nguồn thì báo đội nguồn + dán nhãn dashboard → nếu do transform thì rollback bản trước + chạy lại. Runbook biến kiến thức trong đầu một người thành quy trình cả đội theo được lúc 3h sáng.
SLA/SLO cho sản phẩm dữ liệu: sản phẩm dữ liệu cũng cần cam kết chất lượng như một dịch vụ:
| Chiều | SLO ví dụ (minh hoạ) |
|---|---|
| Freshness | Bảng dư nợ cập nhật xong trước 7h00 mỗi ngày, ≥ 99% số ngày |
| Completeness | ≥ 99.9% bản ghi có đủ trường bắt buộc |
| Availability | Dashboard báo cáo sẵn sàng ≥ 99.5% giờ làm việc |
- SLA (Service Level Agreement): cam kết với người dùng (kèm hệ quả nếu vi phạm).
- SLO (Service Level Objective): mục tiêu nội bộ ta tự đặt để chắc chắn đạt SLA (thường chặt hơn SLA).
Truyền thông khi dữ liệu sai — đặc biệt quan trọng ở ngân hàng
Khi số liệu đã sai và có người dùng, cách báo lỗi quan trọng ngang việc sửa lỗi. Với ngân hàng, dữ liệu sai có thể dẫn tới quyết định tín dụng sai hoặc báo cáo NHNN sai — hệ quả pháp lý. Nguyên tắc:
- Chủ động báo trước khi bị phát hiện: gửi "báo cáo lỗi số liệu" ngay khi xác nhận, nêu rõ bảng/báo cáo nào sai, sai từ khi nào, ảnh hưởng gì, đang xử lý ra sao, khi nào có bản đúng.
- Dán nhãn nghi ngờ lên dashboard/báo cáo bị ảnh hưởng thay vì để người ta dùng số sai.
- Phát hiện sớm là mục tiêu tối thượng: tự phát hiện số liệu bất thường trước khi lãnh đạo hoặc NHNN thấy, để còn kịp sửa và giải trình chủ động thay vì bị chất vấn.
Use case thực tế
Bối cảnh: Đội dữ liệu NCB có bảng fact_du_no phục vụ báo cáo dư nợ toàn hàng gửi Ban điều hành hằng ngày và một phần chỉ tiêu gửi NHNN. Pipeline chạy batch đêm, gộp dữ liệu từ ~40 chi nhánh. Trước đây chỉ dựa vào "job Airflow xanh".
Sự cố: Một sáng, tổng dư nợ giảm 12%. Airflow xanh toàn bộ. Nguyên nhân: hệ nguồn của 3 chi nhánh đổi lịch batch, đẩy dữ liệu muộn, pipeline vẫn "success" trên phần dữ liệu thiếu.
Triển khai observability + incident (số minh hoạ):
- Dựng 5 trụ bằng Elementary + SQL tự viết: freshness theo từng chi nhánh (không chỉ tổng), volume theo ngày so baseline 7 ngày, cảnh báo schema đổi, tỷ lệ null các trường khoá, lineage từ
fact_du_noxuống các dashboard/báo cáo. - Alerting phân severity: thiếu dữ liệu chi nhánh → P1 → gọi on-call; lệch volume nhẹ → P2 Slack.
- Quy trình incident: khi volume một chi nhánh tụt >50% so baseline → alert P1 lúc 5h sáng (trước giờ báo cáo 7h). On-call theo runbook: dừng job publish báo cáo, dán nhãn "đang kiểm tra" lên dashboard, báo đội nguồn, chờ dữ liệu đủ rồi backfill và chạy lại. Báo cáo phát hành lúc 7h20 — trễ 20 phút nhưng đúng số.
- Blameless postmortem: root cause = "không có freshness theo chi nhánh + nguồn đổi lịch không báo". Action item: thêm check freshness theo chi nhánh (đã có), lập kênh thông báo đổi lịch với đội nguồn.
Kết quả sau 1 quý: phát hiện 6 sự cố dữ liệu trước khi người dùng thấy (trước đó 0); thời gian phát hiện trung bình từ "người dùng báo sau vài giờ" xuống "alert trong <15 phút"; không còn lần nào báo cáo NHNN phát hành với số sai. Cách gây dựng thói quen vận hành này trong đội xem thêm ở thực hành đội nhóm.
Ghi nhớ
- Hạ tầng khoẻ ≠ dữ liệu đúng. Job xanh vẫn có thể tạo ra số liệu sai — đó là lý do cần data observability, khác monitoring hạ tầng.
- 5 trụ observability: freshness, volume, schema, distribution, lineage (lineage để khoanh vùng tác động, không phải để phát hiện lỗi).
- Observability ≠ data testing: testing bắt lỗi biết trước (assertion); observability bắt lỗi chưa biết bằng cách tự học baseline trên production. Cần cả hai.
- Công cụ: Monte Carlo, Soda, Elementary (cho dbt), hoặc tự dựng bằng SQL — bắt đầu với vài check freshness/volume/null đã đủ hiệu quả.
- Giám sát pipeline (success/fail, duration, latency, SLA) song song với 5 trụ dữ liệu.
- Alerting đúng: gắn severity, gửi đúng người, kèm ngữ cảnh, chống alert fatigue.
- Incident management: phát hiện → phân loại → kiềm chế (dừng pipeline/rollback/báo consumer) → khắc phục + backfill → root cause → blameless postmortem + action item.
- On-call + runbook để phản ứng nhất quán lúc batch đêm; SLA/SLO cho sản phẩm dữ liệu theo freshness/completeness/availability.
- Truyền thông khi số sai (quan trọng ở ngân hàng): chủ động báo cáo lỗi số liệu, dán nhãn nghi ngờ, và phát hiện sớm trước khi lãnh đạo/NHNN thấy.
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.
Data Engineering là gì, vai trò trong vòng đời dữ liệu, và bức tranh hệ sinh thái công cụ.
Vì sao xử lý phân tán, mô hình Spark (RDD/DataFrame), lazy evaluation, shuffle và tối ưu.
Đả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.