Batch 9 — Batch trong Ngân hàng: EOD & Cuối ngày
Batch 9 — Batch trong Ngân hàng: EOD & Cuối ngày
Bài cuối của series Xử lý Batch & ETL/ELT quy mô lớn. Tám bài trước dạy nguyên lý trung lập công cụ; bài này ráp chúng vào một bài toán thật và khắc nghiệt nhất mà một data/ops engineer ngân hàng gặp mỗi đêm: quy trình cuối ngày (End-of-Day).
Mô hình tinh thần: EOD là một "batch job khổng lồ có deadline pháp lý"
Ngân hàng vận hành trên nguyên tắc ngày làm việc (business day): mọi giao dịch phát sinh trong ngày được ghi nhận theo thời gian thực (real-time), nhưng đến một thời điểm nhất định hệ thống phải "chốt sổ" — tính toán lại toàn bộ trạng thái, sinh ra con số chính thức của ngày đó, rồi mở sang ngày mới. Quá trình này gọi là EOD (End-of-Day) hay COB (Close of Business).
Điểm khác biệt so với một ETL thông thường:
| Batch thường | EOD ngân hàng |
|---|---|
| Trễ vài giờ thường chấp nhận được | Trễ = vi phạm SLA, có thể phạt/không mở được ngày mới |
| Sai thì chạy lại | Sai = sai số dư khách hàng, sai báo cáo NHNN |
| Nguồn ổn định | Nguồn là core banking đang chạy, phải chờ cut-off |
| Idempotent là "nên có" | Idempotent là bắt buộc (đêm nào cũng có sự cố phải rerun) |
Vì vậy EOD là nơi mọi đặc tính batch đã học — idempotency, incremental/CDC, quality gate, backfill — không còn là "best practice" mà là điều kiện sống còn.
Chuỗi giá trị EOD: cut-off → tính toán → cân đối → phân phối
Từng chặng có bản chất batch rõ rệt:
1. Cut-off (chốt cut-off)
Xác định lằn ranh: giao dịch nào tính vào ngày D, giao dịch nào rơi sang D+1. Sau cut-off, các bảng nguồn cho EOD được xem như bất biến (immutable snapshot) — đây chính là điều kiện để pipeline downstream idempotent và có thể replay. Giao dịch đến sau cut-off được xử lý như late-arriving data của ngày mới (liên hệ khái niệm watermark/high-water-mark ở batch-03).
2. Accrual — dự chi lãi (interest accrual)
Mỗi ngày ngân hàng phải ghi nhận trước phần lãi phát sinh dù chưa đến kỳ trả, theo nguyên tắc dồn tích (accrual accounting). Đây là job incremental theo ngày: mỗi ngày cộng thêm một "lát" lãi cho từng hợp đồng.
- Tiền gửi: dự chi lãi phải trả (chi phí của ngân hàng).
- Cho vay: dự thu lãi phải thu (thu nhập của ngân hàng).
Công thức và bản chất nghiệp vụ xem chi tiết ở Huy động vốn 4 — Lãi suất & cách tính lãi. Ở góc độ batch, điểm mấu chốt là idempotent theo (account_id, accrual_date): chạy lại đúng một ngày không được cộng lãi hai lần.
3. Tính lãi định kỳ, đáo hạn, nhập gốc
Với hợp đồng đến kỳ trả lãi/đáo hạn trong ngày D: hiện thực hoá lãi dồn tích thành dòng tiền, tất toán/quay vòng sổ tiết kiệm. Đây là event-driven bên trong một batch: lọc tập hợp đồng "đến hạn hôm nay" rồi xử lý.
4. Phân loại nợ & trích lập dự phòng
Theo Thông tư của NHNN (phân nhóm nợ 1–5 dựa trên số ngày quá hạn và định tính), batch quét toàn bộ danh mục cho vay, cập nhật nhóm nợ, tính dự phòng rủi ro (DPRR). Đây là job full-scan có trạng thái lịch sử — cần dữ liệu ngày trước để so sánh chuyển nhóm.
5. Hạch toán & cân đối Sổ Cái (GL)
Mọi bút toán tự động (lãi, phí, phân loại nợ) đổ về General Ledger. Quality gate cứng: Tổng phát sinh Nợ = Tổng phát sinh Có và số dư GL khớp số dư chi tiết (sub-ledger). Lệch một đồng → dừng dây chuyền. Đây là hiện thân trực tiếp của Data Quality — quality gate chặn pipeline.
6. Sinh sao kê (statement)
Sau khi số liệu đã "chốt", batch sinh sao kê tài khoản cho khách hàng: liệt kê giao dịch trong ngày/kỳ, số dư đầu–cuối, lãi. Đặc tính batch: partition theo khách hàng/chi nhánh, có thể chạy song song, tái tạo lại được từ snapshot đã chốt.
7. Đối soát (reconciliation)
Đối chiếu giữa các hệ thống (core ↔ thẻ ↔ NAPAS ↔ ngân hàng đối tác ↔ GL). Liệt kê chênh lệch, tạo bút toán treo/điều chỉnh. Đây là batch so khớp hai tập — kinh điển cho pattern anti-join.
8. Rollover / Cutover — mở ngày mới
Chỉ khi tất cả quality gate PASS, hệ thống mới đổi business date và mở ngày giao dịch mới. Đây là "commit" của cả batch EOD — hoặc toàn bộ thành công, hoặc giữ nguyên ngày cũ để xử lý sự cố.
Batch window & áp lực thời gian
EOD sống trong một cửa sổ thời gian (batch window) hẹp: sau khi đóng quầy/cut-off cho tới trước giờ mở cửa hôm sau, trừ đi thời gian dành cho sao lưu, đối soát liên ngân hàng và gửi báo cáo. Trễ EOD gây hiệu ứng domino: DWH nạp trễ → báo cáo NHNN trễ → chi nhánh mở cửa muộn.
Kỹ thuật giảm áp lực batch window (đã học trong series):
- Incremental thay vì full: chỉ xử lý delta trong ngày (batch-03) — trừ các job buộc phải full-scan như phân loại nợ.
- Song song hoá theo partition: sao kê, dự chi lãi chia theo chi nhánh/khoảng account_id.
- Tách "pre-EOD": những tính toán không phụ thuộc cut-off (làm giàu dữ liệu tham chiếu) đẩy lên chạy sớm.
- Đường găng (critical path): tối ưu đúng chuỗi quyết định giờ hoàn thành, không tối ưu tràn lan.
Chuỗi phụ thuộc: Core Banking → DWH → Báo cáo NHNN
EOD không phải một job mà là một DAG phụ thuộc trải qua nhiều hệ thống. Việc orchestrate (Airflow/Control-M...) phải tôn trọng thứ tự này — mỗi tầng chỉ chạy khi tầng trước đã chốt và đạt chất lượng.
Chuỗi này đúng nguyên tắc medallion (bronze → silver → gold) và cho thấy lý do batch banking khắt khe: báo cáo NHNN đứng cuối và có deadline pháp lý, nên mọi trễ/sai ở đầu nguồn đều dồn về đây. Giá trị mà DWH tạo ra ở tầng gold — phân tích danh mục, hành vi, rủi ro — được bàn ở Nghiệp vụ 8 — Dữ liệu & Phân tích.
Cutover ngày & xử lý ngày nghỉ
Hai vấn đề "lịch" khiến batch banking khác biệt:
Cutover (đổi ngày): business date là một biến hệ thống, không phải current_date của server. EOD kết thúc bằng việc tăng business date. Mọi job downstream phải tham số hoá theo business_date (không hard-code hôm nay) — đây là điều kiện để backfill/replay một ngày trong quá khứ mà không lệ thuộc đồng hồ thực.
Ngày nghỉ (holiday / weekend):
- Một số ngân hàng vẫn chạy EOD cuối tuần (T7/CN) nhưng không mở ngày làm việc mới — hoặc gộp phát sinh vào ngày làm việc kế tiếp.
- Dự chi lãi vẫn cộng đủ số ngày dương lịch kể cả ngày nghỉ (lãi không nghỉ lễ), nên logic accrual phải dựa vào lịch dương/day-count, không dựa vào "có chạy batch hay không".
- Ngày nghỉ lễ dài (Tết) là bài toán holiday calendar trong orchestration: bỏ qua/dồn các job phụ thuộc ngày làm việc, và là cao điểm cho backfill khi có sự cố kéo dài.
Nguyên tắc vàng: tách "ngày lịch" khỏi "ngày làm việc" và luôn truyền business_date như tham số → job trở nên idempotent & backfill-able cho đúng một ngày bất kỳ.
Pipeline EOD mẫu — vận dụng đặc tính batch đã học
Ghép các nguyên lý của series thành một job dự chi lãi tiền gửi.
Idempotent theo (account_id, business_date)
Dùng "delete-then-insert theo partition ngày" thay vì insert thuần — chạy lại đúng ngày D không nhân đôi:
-- Job accrual cho mot business_date, idempotent va incremental
-- Xoa ket qua cu cua dung ngay D (neu rerun) roi tinh lai
DELETE FROM gl.interest_accrual
WHERE business_date = :D;
INSERT INTO gl.interest_accrual
(account_id, business_date, principal, rate, day_count_base, accrued_amount)
SELECT
a.account_id,
:D AS business_date,
b.closing_balance AS principal,
r.annual_rate AS rate,
r.day_count_base AS day_count_base, -- 365 hoac 360
-- lai mot ngay = du no x lai suat nam / co so nam
ROUND(b.closing_balance * r.annual_rate / r.day_count_base, 2) AS accrued_amount
FROM dwh.account a
JOIN dwh.eod_balance b
ON b.account_id = a.account_id
AND b.business_date = :D -- snapshot da chot sau cut-off
JOIN dwh.interest_rate r
ON r.product_id = a.product_id
AND :D BETWEEN r.valid_from AND COALESCE(r.valid_to, DATE '9999-12-31') -- SCD2
WHERE a.account_status = 'ACTIVE'
AND a.account_type = 'DEPOSIT';
Vài điểm nối với series:
business_date = :Dđược truyền vào → backfill một ngày quá khứ chỉ là gọi lại job vớiDkhác.eod_balancelà snapshot bất biến sau cut-off → nguồn ổn định cho replay.- Bảng lãi suất là SCD Type 2 (
valid_from/valid_to) → tính đúng lãi suất tại thời điểm ngày D, không bị "lãi suất hôm nay áp cho quá khứ".
Quality gate: cân đối GL trước khi cho chạy tiếp
Sau khi hạch toán, chặn cutover nếu Nợ ≠ Có:
-- Gate: tong phat sinh No phai bang tong phat sinh Co trong ngay D
SELECT
business_date,
SUM(CASE WHEN dr_cr = 'D' THEN amount ELSE 0 END) AS total_debit,
SUM(CASE WHEN dr_cr = 'C' THEN amount ELSE 0 END) AS total_credit,
SUM(CASE WHEN dr_cr = 'D' THEN amount ELSE -amount END) AS diff
FROM gl.journal_entry
WHERE business_date = :D
GROUP BY business_date
HAVING SUM(CASE WHEN dr_cr = 'D' THEN amount ELSE -amount END) <> 0;
-- Tra ve >0 dong => LECH => gate FAIL => dung pipeline, canh bao Ops
Đối soát: anti-join tìm chênh lệch
Đối soát giao dịch thẻ giữa core và NAPAS bằng full outer join, liệt kê lệch:
-- Doi soat: khop theo txn_ref; liet ke giao dich chi co o 1 phia hoac lech tien
SELECT
COALESCE(c.txn_ref, n.txn_ref) AS txn_ref,
c.amount AS core_amount,
n.amount AS napas_amount,
CASE
WHEN n.txn_ref IS NULL THEN 'MISSING_IN_NAPAS'
WHEN c.txn_ref IS NULL THEN 'MISSING_IN_CORE'
WHEN c.amount <> n.amount THEN 'AMOUNT_MISMATCH'
END AS recon_status
FROM (SELECT * FROM core.card_txn WHERE business_date = :D) c
FULL OUTER JOIN
(SELECT * FROM napas.settlement WHERE business_date = :D) n
ON c.txn_ref = n.txn_ref
WHERE c.txn_ref IS NULL
OR n.txn_ref IS NULL
OR c.amount <> n.amount; -- chi giu cac dong LECH -> tao but toan treo
Bản đồ đặc tính batch ↔ chặng EOD
| Đặc tính (bài trong series) | Áp dụng trong EOD |
|---|---|
| Tổng quan ETL/ELT — batch-01 | EOD là ELT nhiều tầng: extract snapshot → load bronze → transform silver/gold |
| Incremental/CDC — batch-03 | Accrual, sao kê xử lý delta theo ngày; cut-off = watermark |
| Idempotency — batch-04 | Delete-then-insert theo business_date; rerun đêm sự cố an toàn |
| Data Quality — batch-08 | Cân đối GL, đối soát là gate cứng chặn cutover |
| Backfill/reprocessing | Chạy lại một business_date quá khứ khi phát hiện sai lệch |
| Partitioning & song song | Sao kê/accrual chia theo chi nhánh/account range |
Use case thực tế
Bối cảnh minh hoạ (số liệu minh hoạ, không phải số thật của NCB): một ngân hàng bán lẻ với ~3 triệu tài khoản tiền gửi và ~400 nghìn hợp đồng vay. Batch window EOD kéo dài 18:00–23:30, đường găng nằm ở chuỗi accrual → cân đối GL → đối soát NAPAS → nạp DWH → báo cáo NHNN.
Sự cố điển hình: 21:40 job đối soát báo AMOUNT_MISMATCH cho một lô giao dịch thẻ do file settlement NAPAS về thiếu một batch. Nhờ pipeline idempotent theo business_date, Ops chỉ cần: nhận file bổ sung, rerun đúng ngày D cho nhánh đối soát và các job phụ thuộc (đối soát → nạp DWH → báo cáo) — không phải chạy lại toàn bộ EOD, không nhân đôi bút toán. Cutover được lùi 40 phút nhưng báo cáo NHNN vẫn kịp trước deadline sáng hôm sau. Không có tính idempotent + tham số business_date, tình huống này buộc phải chạy lại từ đầu và gần như chắc chắn trễ deadline.
Giá trị dài hạn: snapshot bronze bất biến mỗi ngày cho phép backfill khi kiểm toán yêu cầu dựng lại số dư/lãi của một ngày trong quá khứ, và cho phép tầng gold phục vụ phân tích rủi ro danh mục (dep-08).
Ghi nhớ
- EOD/COB = batch có deadline pháp lý: trễ hoặc sai không chỉ là sự cố kỹ thuật mà là vi phạm SLA/quy định NHNN.
- Cut-off tạo snapshot bất biến — nền tảng để mọi job downstream idempotent và replay được.
- Dự chi lãi (accrual) là job incremental theo ngày; phải idempotent theo
(account_id, business_date)để rerun không cộng lãi hai lần. - Cân đối GL và đối soát là quality gate cứng: Nợ=Có và khớp liên ngân hàng mới được cutover.
- Luôn tham số hoá theo
business_date, tách "ngày lịch" khỏi "ngày làm việc" → backfill và xử lý ngày nghỉ trở nên đơn giản. - Lãi vẫn cộng đủ ngày dương lịch kể cả ngày nghỉ; logic accrual dựa trên day-count, không dựa trên lịch chạy batch.
- Chuỗi phụ thuộc core → DWH → báo cáo NHNN là một DAG; báo cáo đứng cuối nên tối ưu đường găng, không tối ưu tràn lan.
- Đây là bài tổng kết: EOD là nơi idempotency, incremental, quality gate và backfill của batch-01..08 hội tụ thành yêu cầu bắt buộc.
Nguồn tham khảo
- Joe Reis & Matt Housley — Fundamentals of Data Engineering (O'Reilly). Vòng đời dữ liệu, batch vs streaming, idempotency, quality.
- Martin Kleppmann — Designing Data-Intensive Applications (O'Reilly). Batch processing, exactly-once, reprocessing/backfill.
- Ralph Kimball & Margy Ross — The Data Warehouse Toolkit. Dimensional modeling, SCD, conformed dimensions cho DWH ngân hàng.
- Apache Airflow — tài liệu chính thức: https://airflow.apache.org/docs/ (DAG, scheduling, backfill, holiday/calendar).
- dbt — tài liệu chính thức: https://docs.getdbt.com/ (incremental models, tests/quality gate).
- Great Expectations — tài liệu chính thức: https://docs.greatexpectations.io/ (data validation cho quality gate).
- Ngân hàng Nhà nước Việt Nam — quy định phân loại nợ & trích lập dự phòng rủi ro (Thông tư về phân loại tài sản có): https://sbv.gov.vn/
Bài viết liên quan
So sánh các định dạng dữ liệu (CSV, JSON, XML, Avro, Parquet, ORC) và lý do lưu theo cột nhanh hơn cho phân tích. Bài đi sâu vào row vs columnar storage, nén (Snappy/gzip/zstd), schema evolution, OLTP vs OLAP, object storage và partitioning để tối ưu chi phí lẫn tốc độ truy vấn.
Data Engineering là ngành xây dựng và vận hành hệ thống biến dữ liệu thô thành dữ liệu sạch, tin cậy, sẵn sàng cho phân tích và AI. Bài giới thiệu vai trò Data Engineer trong vòng đời dữ liệu (nguồn → ingestion → storage → transformation → serving), phân biệt với Analyst/Scientist/ML Engineer, bức tranh hệ sinh thái công cụ và bài toán đưa dữ liệu core banking sang kho phân tích.
Stream processing là gì, khác biệt batch vs stream (bounded/unbounded), micro-batch (Spark) vs true streaming, Apache Flink là gì và định vị so với Spark Structured Streaming và Kafka Streams. Kiến trúc runtime JobManager/TaskManager, các tầng API, triết lý streaming-first và bối cảnh phát hiện gian lận ngân hàng.
Vì sao một máy không đủ và cần xử lý phân tán: từ MapReduce, Hadoop/HDFS đến Apache Spark in-memory. Kiến trúc driver–executor–cluster manager, các mức trừu tượng RDD/DataFrame/Dataset, cơ chế lazy evaluation với DAG, và vì sao shuffle (wide dependency) là phần tốn kém nhất. Kèm PySpark, Spark SQL và các kỹ thuật tối ưu (partition, broadcast join, cache, chống skew) cùng khi nào KHÔNG nên dùng Spark.
Cảm nhận của bạn
Bình luận
Chưa có bình luận. Hãy là người đầu tiên chia sẻ!