DataOps nâng cao 22 — Data SLA & xử lý Data Downtime
Từ "quan sát được" tới "cam kết được"
Hai bài Observability & DRE và Observability & Incident dựng khung nhìn thấy dữ liệu hỏng và quy trình sự cố cơ bản. Bài này đi sâu hơn vào phần vận hành cam kết: biến "chúng tôi cố gắng dữ liệu đúng" thành một hợp đồng mức dịch vụ có số đo, có ngân sách lỗi, có người trực, và một playbook xử lý sự cố đủ chi tiết để đội trực chạy lúc 2h sáng mà không phải suy nghĩ.
Câu hỏi trung tâm không còn là "dữ liệu có quan sát được không?" mà là "chúng ta cam kết dữ liệu tin cậy tới mức nào, đo bằng gì, và khi vi phạm thì xử lý ra sao?". Ở ngân hàng, đây là câu hỏi giải trình được với Ban điều hành và NHNN.
Data Downtime là gì
Data Downtime là khoảng thời gian dữ liệu sai / thiếu / trễ / không dùng được — tức khoảng mà một consumer nếu tin vào dữ liệu sẽ ra quyết định sai. Đây là khái niệm song song với "downtime" của hệ thống phần mềm, nhưng nguy hiểm hơn ở chỗ: web sập thì ai cũng thấy ngay; còn một bảng báo cáo vẫn trả về số nhưng số sai thì trông như đang "up" — người dùng cứ ra quyết định trên nền cát.
Hậu quả của data downtime ở ngân hàng:
- Quyết định sai: duyệt tín dụng, định giá rủi ro, cắt hạn mức trên số liệu lệch.
- Mất niềm tin: một lần Ban điều hành phát hiện dashboard sai, họ quay lại xin file Excel — công sức data platform đổ sông.
- Rủi ro tuân thủ: báo cáo NHNN nộp sai số phải đính chính, có thể bị xử phạt và tổn hại uy tín.
Công thức trực giác
Có thể ước lượng data downtime theo trực giác (không phải công thức chính xác tuyệt đối, dùng để định hướng cải thiện):
Data Downtime ≈ Số sự cố × (Thời gian phát hiện + Thời gian khắc phục)
(N) (TTD) (TTR)
Ba đòn bẩy để giảm downtime hiện rõ: giảm số sự cố (test/contract/quy trình đổi nguồn), giảm TTD — Time To Detect (observability, alert đúng), giảm TTR — Time To Resolve (runbook, rollback nhanh, WAP). Cải thiện observability chủ yếu đánh vào TTD; cải thiện quy trình xử lý đánh vào TTR; cải thiện chất lượng đầu vào đánh vào N.
SLI / SLO / SLA cho một Data Product
Coi mỗi bảng/báo cáo quan trọng là một data product có mức dịch vụ cam kết. Ba khái niệm cần tách bạch:
| Khái niệm | Là gì | Ví dụ dữ liệu |
|---|---|---|
| SLI (Indicator) | Số đo được phản ánh chất lượng | freshness = độ trễ giờ; completeness = % bản ghi đủ |
| SLO (Objective) | Mục tiêu nội bộ đặt trên SLI | "freshness ≤ 2h trong 99% ngày làm việc" |
| SLA (Agreement) | Cam kết với consumer, có hệ quả nếu vi phạm | "báo cáo rủi ro sẵn sàng trước 8h, sai thì đính chính + thông báo trong 30') |
Nguyên tắc: SLA lỏng hơn SLO một biên an toàn. Nếu cam kết với sếp SLA "đúng 8h", nội bộ nên đặt SLO "xong 7h30" để còn thời gian phản ứng.
Bốn nhóm SLI cốt lõi cho dữ liệu
- Freshness — dữ liệu mới nhất tới thời điểm nào; đo bằng
MAX(created_at)so với now. Trễ = downtime kiểu "chưa có số". - Completeness — có đủ bản ghi/đủ trường không; đo bằng % dòng, % null của trường khoá, số dòng so kỳ vọng. Thiếu = downtime kiểu "số bị hụt".
- Độ chính xác (accuracy) — số có đúng không; đo bằng reconciliation với nguồn gốc, tổng kiểm chứng (control total), quy tắc nghiệp vụ. Sai = downtime nguy hiểm nhất vì trông vẫn "có số".
- Availability — bảng/báo cáo có truy vấn được, đúng schema, đúng hạt không; đo bằng % thời gian query thành công đúng contract.
SLI freshness — check chạy được
-- ▶ Chạy được
SELECT
MAX(created_at) AS latest_txn,
(CURRENT_DATE - MAX(created_at)::date) AS days_stale,
ROUND(EXTRACT(EPOCH FROM (now() - MAX(created_at))) / 3600.0, 1) AS hours_stale,
COUNT(*) FILTER (WHERE created_at::date = CURRENT_DATE) AS rows_today
FROM transactions;
days_stale > 0 hoặc hours_stale vượt ngưỡng SLO vào giờ báo cáo nghĩa là đang vi phạm freshness — mở sự cố. Ở mức thực chiến, freshness nên đo theo lát cắt (từng nguồn, từng chi nhánh) vì một nguồn trễ dễ bị "che" bởi phần còn lại đúng hạn.
SLI completeness — tỷ lệ null của trường khoá
Completeness đo mức độ "đủ" của bản ghi. Ví dụ đo tỷ lệ tài khoản thiếu số dư hoặc thiếu loại tiền — hai trường không được null nếu bảng lành mạnh:
-- ▶ Chạy được
SELECT
COUNT(*) AS total_rows,
COUNT(*) FILTER (WHERE balance IS NULL) AS null_balance,
COUNT(*) FILTER (WHERE currency IS NULL OR currency = '') AS missing_currency,
ROUND(100.0 * COUNT(*) FILTER (WHERE balance IS NULL)
/ NULLIF(COUNT(*), 0), 2) AS pct_null_balance,
CASE
WHEN COUNT(*) FILTER (WHERE balance IS NULL) = 0
AND COUNT(*) FILTER (WHERE currency IS NULL OR currency = '') = 0
THEN 'PASS' ELSE 'FAIL'
END AS completeness_slo
FROM accounts;
SLO completeness kiểu "pct_null_balance = 0 trên trường khoá" cho ra kết quả nhị phân PASS/FAIL — dễ đưa lên bảng theo dõi.
Error budget — quản lý bằng ngân sách, không bằng cảm tính
SLO không phải "100%" (bất khả thi và tốn kém vô lý). Nếu SLO = 99% ngày làm việc đúng hạn, thì 1% còn lại là error budget — ngân sách được phép hỏng. Quản lý bằng budget cho ra quyết định khách quan:
- Còn budget nhiều → ưu tiên tính năng mới, chấp nhận thay đổi rủi ro có kiểm soát.
- Cháy budget (đã vi phạm quá mức cho phép) → đóng băng thay đổi, dồn lực vào ổn định (thêm test, thêm monitor, sửa nợ kỹ thuật) cho tới khi budget hồi.
Ví dụ: báo cáo rủi ro tín dụng đặt SLO freshness 99,5% ngày làm việc/quý (~63 ngày). Budget ≈ 0,5% × 63 ≈ 0,3 ngày trễ được phép/quý. Trễ 1 buổi sáng là gần cháy — kích hoạt cơ chế đóng băng.
Tier hoá data product
Không phải bảng nào cũng đáng cam kết 99,9%. Phân tier theo mức quan trọng để dồn công sức đúng chỗ:
| Tier | Ví dụ | SLO minh hoạ | On-call |
|---|---|---|---|
| Tier 0 / Vàng | Báo cáo NHNN, báo cáo rủi ro, số liệu Ban điều hành | freshness ≤ 2h, accuracy reconcile 100%, availability 99,9% | có, escalation nhanh |
| Tier 1 / Bạc | Dashboard nghiệp vụ chính, mart bán hàng | freshness ≤ 6h, completeness cao | giờ hành chính |
| Tier 2 / Đồng | Bảng thăm dò, sandbox phân tích | best-effort | không cam kết |
Tier quyết định độ ưu tiên khi sự cố và mức đầu tư test/monitor. Xem thêm cách gắn cam kết với Quality & Data Contracts.
Incident Management thực chiến — Playbook
Khi một SLI vi phạm SLO, ta mở data incident. Playbook chuẩn hoá các bước để đội trực không phải nghĩ:
1. Phát hiện (Detect)
Hai đường vào: chủ động (alert từ observability — freshness/volume/schema/distribution) và bị động (người dùng báo "số này trông sai"). Mục tiêu DRE là luôn phát hiện trước consumer — mỗi lần user báo trước là một lỗ hổng monitoring cần vá.
2. Phân loại severity (Triage)
Gán SEV theo tác động, không theo cảm tính:
| Severity | Tiêu chí | Ví dụ ngân hàng |
|---|---|---|
| SEV1 | Số liệu Tier 0 sai/thiếu, ảnh hưởng quyết định hoặc báo cáo cơ quan quản lý | Báo cáo NHNN trễ hạn nộp / sai tổng dư nợ |
| SEV2 | Data product quan trọng sai nhưng có đường vòng, chưa tới hạn báo cáo | Dashboard rủi ro lệch nhưng còn 6h trước cuộc họp |
| SEV3 | Lỗi nhỏ, bảng phụ, không ảnh hưởng quyết định | Trễ mart thăm dò nội bộ |
SEV quyết định ai được gọi, gọi nhanh cỡ nào, và có được đánh thức lúc nửa đêm không.
3. Thông báo consumer sớm (Comms)
Đây là bước data-specific quan trọng nhất và hay bị bỏ. Khác downtime phần mềm (user tự thấy web sập), data downtime vô hình — nếu không báo, consumer vẫn dùng số sai. Nguyên tắc:
- Báo sớm, báo rõ: "Báo cáo X có thể sai từ [giờ], đang điều tra, đừng dùng để ra quyết định, cập nhật sau 30'."
- Gắn banner cảnh báo ngay trên dashboard nếu có thể (đánh dấu bảng "under investigation").
- Mục tiêu số một: ngăn người dùng ra quyết định trên số sai, kể cả khi chưa biết nguyên nhân.
4. Kiềm chế (Contain)
Chặn thiệt hại lan rộng trước khi hiểu hết nguyên nhân:
- Dừng pipeline đang đẩy dữ liệu bẩn xuống hạ nguồn.
- Rollback về phiên bản dbt/artifact tốt gần nhất.
- Giữ bản tốt cũ nhờ mẫu WAP (Write-Audit-Publish): dữ liệu mới ghi vào vùng staging, chỉ publish khi qua audit — nếu audit fail thì consumer vẫn thấy bản publish cũ (đúng), không thấy bản mới (sai). Xem Environments & WAP.
WAP biến "kiềm chế" từ thao tác khẩn cấp thành hành vi mặc định: bản sai đơn giản không bao giờ tới tay consumer.
5. Khắc phục (Fix)
Sửa gốc rồi backfill lại phần dữ liệu hỏng, chạy lại kiểm tra chất lượng, và publish bản đúng. Ghi lại mốc "resolved" khi SLI trở lại trong SLO — đây là mốc tính TTR.
6. RCA — Root Cause Analysis
Sau khi hết chảy máu, đào nguyên nhân gốc bằng 5 Whys (hỏi "tại sao" tới khi chạm nguyên nhân hệ thống, không dừng ở triệu chứng). Phân loại nguyên nhân giúp tìm biện pháp phòng ngừa đúng:
| Nhóm nguyên nhân | Biểu hiện | Phòng ngừa điển hình |
|---|---|---|
| Đổi nguồn / schema drift | Nguồn thượng nguồn đổi cột/kiểu/nghĩa | schema check, data contract, cảnh báo thượng nguồn |
| Lỗi code / logic | Bug trong dbt/transform | test, code review, CI/CD |
| Dữ liệu trễ / muộn | Batch nguồn chưa đổ đủ | freshness monitor, sensor chờ nguồn |
| Hạ tầng | Warehouse quá tải, job timeout, mạng | retry, giám sát tài nguyên, hạ tải |
Ví dụ 5 Whys: Tổng dư nợ sai → vì thiếu một chi nhánh → vì file nguồn chi nhánh đó tới trễ → vì pipeline không chờ mà chạy đúng giờ → vì không có sensor kiểm tra đủ nguồn. → Biện pháp gốc: thêm completeness-gate chờ đủ nguồn trước khi tính tổng.
7. Hậu kiểm & phòng ngừa (Prevent)
Mỗi sự cố phải sinh ra ít nhất một test/monitor mới để lần sau tự bắt được. Đây là vòng học: sự cố hôm nay → test ngày mai. Không thêm phòng ngừa = chấp nhận sự cố sẽ tái diễn.
Phát hiện sự cố bằng volume-baseline
Một trong những check "bắt sự cố sớm" hiệu quả nhất: so số dòng mỗi ngày với baseline động (trung bình trượt), cờ khi lệch quá biên. Ngưỡng cứng sẽ sai vào cuối tuần/lễ; baseline động tự thích nghi:
-- ▶ 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;
Dòng ANOMALY là tín hiệu vào bước Detect của playbook: volume lệch > 30% so trung bình 7 ngày → nghi thiếu nguồn hoặc nhân đôi dữ liệu → mở incident, phân loại SEV.
On-call bền vững & Runbook
On-call cho data khác on-call phần mềm ở nhịp: nhiều sự cố là batch đêm, không phải realtime 24/7. Nguyên tắc bền vững để đội không kiệt sức:
- Chỉ đánh thức khi cần: chỉ SEV1 (Tier 0) mới page lúc nửa đêm; SEV2/3 chờ giờ hành chính.
- Giảm nhiễu: alert phải actionable — mỗi page phải có runbook đi kèm. Alert không hành động được thì tắt hoặc hạ cấp.
- Escalation rõ: on-call → tech lead → data platform lead → chủ quản nghiệp vụ, mỗi bậc có thời gian chờ (ví dụ 15' không phản hồi thì lên bậc).
Runbook mẫu — "Freshness Tier 0 vi phạm"
RUNBOOK: Báo cáo rủi ro trễ (freshness SLO vi phạm)
1. XÁC NHẬN: chạy check freshness → days_stale/hours_stale thực tế?
2. PHÂN LOẠI: Tier 0 + qua giờ báo cáo → SEV1.
3. COMMS: gửi mẫu "báo cáo có thể trễ, đừng dùng, cập nhật sau 30'".
4. KHOANH VÙNG: nguồn nào trễ? (check freshness theo lát cắt)
5. KIỀM CHẾ: giữ bản publish hôm qua (WAP), KHÔNG publish bản thiếu.
6. FIX: chờ/kích nguồn trễ → backfill → chạy lại audit → publish.
7. ĐÓNG: xác nhận SLI về SLO, ghi mốc resolved, hẹn postmortem.
Runbook tốt = người trực mới cũng xử lý được mà không cần gọi kiến trúc sư.
Blameless Postmortem & đo lường độ tin cậy
Blameless postmortem: sau SEV1/SEV2, họp mổ xẻ tập trung vào hệ thống và quy trình, không đổ lỗi cá nhân. Con người mắc lỗi; hệ thống tốt là hệ thống chịu được lỗi người. Đổ lỗi khiến người ta giấu sự cố — thứ độc hại nhất cho độ tin cậy. Mỗi postmortem chốt action item có chủ và deadline.
Đo độ tin cậy bằng số để cải tiến có định hướng:
| Chỉ số | Ý nghĩa | Mục tiêu |
|---|---|---|
| MTTD (Mean Time To Detect) | Trung bình phát hiện sau khi hỏng | càng nhỏ càng tốt — đo hiệu quả monitoring |
| MTTR (Mean Time To Resolve) | Trung bình từ phát hiện đến sửa xong | đo hiệu quả playbook/runbook |
| Số sự cố (theo tier/SEV) | Tần suất hỏng | xu hướng giảm = chất lượng lên |
| Data uptime | % thời gian data product trong SLO | báo cáo lên lãnh đạo |
Văn hoá reliability đóng khung tất cả: cam kết đo được, minh bạch khi hỏng, học từ sự cố thay vì trừng phạt. Đó là thứ biến một đội "hay chữa cháy" thành đội "ít cháy".
Use case thực tế
Bối cảnh NCB, báo cáo tổng dư nợ theo chi nhánh nộp NHNN, hạn 8h sáng — data product Tier 0, SLO freshness ≤ 2h và accuracy reconcile 100%.
- 06:12 — alert volume-baseline: số giao dịch hôm qua lệch −18% so trung bình 7 ngày (
status = ANOMALY). Đồng thời freshness check chodays_stale = 0nhưng completeness theo lát cắt thấy thiếu 1/40 chi nhánh. TTD ≈ vài phút nhờ monitor (không phải chờ user báo). - 06:15 — Triage: Tier 0, qua ngưỡng, còn <2h tới hạn nộp → SEV1.
- 06:18 — Comms: gửi cán bộ báo cáo: "Tổng dư nợ có thể thiếu 1 chi nhánh, chưa nộp, cập nhật 06:45." Gắn banner "under investigation" trên dashboard → ngăn nộp số sai.
- 06:20 — Contain: nhờ WAP, bản thiếu chưa publish; consumer vẫn thấy bản hôm qua (đúng), không thấy bản thiếu.
- 06:25 — Fix: khoanh vùng → file chi nhánh tới trễ do sự cố mạng nguồn; kích lại ingest, backfill, chạy lại audit reconcile control-total = khớp → publish.
- 06:40 — Resolve: SLI về SLO, nộp đúng hạn. MTTR ≈ 28'.
- RCA (5 Whys): thiếu chi nhánh → file trễ → pipeline không chờ đủ nguồn → không có completeness-gate. Nguyên nhân nhóm "dữ liệu trễ".
- Prevent: thêm sensor chờ đủ 40/40 nguồn trước khi tính tổng; thêm alert freshness theo lát cắt chi nhánh. Postmortem blameless, action item có chủ.
Kết quả: nhờ comms + WAP, không có số sai nào nộp NHNN dù nguồn trễ — đúng mục tiêu tối thượng của DRE.
Ghi nhớ
- Data Downtime = khoảng dữ liệu sai/thiếu/trễ/không dùng được; nguy hiểm vì thường vô hình (bảng vẫn trả số). Trực giác:
Downtime ≈ N × (TTD + TTR). - Tách SLI (đo được) / SLO (mục tiêu nội bộ) / SLA (cam kết với consumer); SLA nên lỏng hơn SLO một biên an toàn.
- Bốn nhóm SLI: freshness, completeness, accuracy, availability. Đặt SLO theo tier — Tier 0 (báo cáo NHNN) cam kết cao nhất.
- Error budget cho quyết định khách quan: còn budget → làm mới; cháy budget → đóng băng thay đổi, dồn lực ổn định.
- Playbook: detect → triage (SEV1-3) → comms sớm (ngăn dùng số sai) → contain (dừng/rollback/WAP) → fix (backfill) → RCA (5 whys) → prevent.
- Comms là bước data-specific quan trọng nhất — data downtime vô hình, không báo thì consumer vẫn ra quyết định sai.
- RCA phân loại nguyên nhân (đổi nguồn/schema, lỗi code, dữ liệu trễ, hạ tầng) để phòng ngừa đúng; mỗi sự cố sinh ≥1 test/monitor mới.
- On-call bền vững: chỉ page SEV1, alert phải actionable + có runbook, escalation rõ. Blameless postmortem để người ta không giấu sự cố.
- Đo bằng MTTD, MTTR, số sự cố, data uptime; văn hoá reliability = cam kết đo được + minh bạch + học từ lỗi.
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.