Data Engineering 10 — Mô hình hoá Kho dữ liệu nâng cao
Tại sao mô hình hoá kho dữ liệu vẫn quan trọng
Nhiều người nghĩ rằng với kho dữ liệu đám mây (cloud data warehouse) gần như vô hạn về tính toán và lưu trữ, việc mô hình hoá dữ liệu đã lỗi thời — "cứ ném hết vào một bảng to (wide table) rồi query là xong". Đây là sai lầm tốn kém. Mô hình hoá tốt mang lại ba thứ mà sức mạnh phần cứng không mua được: ý nghĩa kinh doanh rõ ràng (ai cũng hiểu một con số tính như thế nào), tính nhất quán (cùng một chỉ tiêu cho cùng một kết quả ở mọi báo cáo), và khả năng tiến hoá (thêm nguồn dữ liệu mới mà không phá vỡ cái đã có).
Bài viết này nối tiếp phần BI về dimensional modeling nhưng đào sâu phía engineering: chúng ta sẽ so sánh ba trường phái thiết kế, mổ xẻ các loại fact table, đi qua từng kiểu SCD, và áp dụng vào bối cảnh ngân hàng — nơi dữ liệu vừa nhiều, vừa nhạy cảm với lịch sử thay đổi.
Toàn bộ DDL trong bài là (minh hoạ, không chạy trong sandbox) — mục đích để bạn thấy cấu trúc, không phải để thực thi.
Ôn lại nhanh: Dimensional Modeling
Dimensional modeling tổ chức dữ liệu thành fact (sự kiện đo lường được — bao nhiêu, mấy lần) và dimension (ngữ cảnh mô tả — ai, cái gì, khi nào, ở đâu). Mô hình kinh điển là star schema: một bảng fact ở giữa, các bảng dimension toả ra xung quanh, mỗi dimension nối với fact qua một khoá.
Ưu điểm của star schema là dễ hiểu với người dùng nghiệp vụ và tối ưu cho query phân tích (ít join, các engine cột tối ưu tốt). Snowflake schema là biến thể chuẩn hoá dimension thành nhiều bảng con — tiết kiệm chỗ nhưng thêm join, ngày nay hiếm khi đáng đánh đổi.
Ba trường phái thiết kế
Có ba triết lý lớn về cách xây kho dữ liệu doanh nghiệp. Không có cái nào "đúng tuyệt đối" — lựa chọn phụ thuộc vào quy mô, tốc độ thay đổi nguồn, yêu cầu kiểm toán (audit), và năng lực đội ngũ.
Kimball — Bottom-up, star schema
Ralph Kimball chủ trương xây từ dưới lên: bắt đầu từ các data mart theo từng quy trình nghiệp vụ (giao dịch, khoản vay, thẻ...), mỗi mart là một star schema. Chìa khoá để các mart không trở thành ốc đảo là conformed dimension — những dimension dùng chung (khách hàng, ngày, sản phẩm) được định nghĩa thống nhất một lần và tái sử dụng ở mọi fact. Khi đó báo cáo ở mart khác nhau vẫn có thể "trộn" được vì cùng chiều khách hàng, cùng chiều thời gian.
Ưu điểm: triển khai nhanh, ra giá trị sớm, người nghiệp vụ tiếp cận dễ. Nhược: nếu quản trị conformed dimension lỏng lẻo, các mart dễ lệch nhau theo thời gian.
Inmon — Top-down, 3NF enterprise warehouse
Bill Inmon chủ trương xây một Enterprise Data Warehouse (EDW) chuẩn hoá theo dạng 3NF (third normal form) làm "nguồn sự thật duy nhất" cho toàn doanh nghiệp, rồi mới derive các data mart dimensional phục vụ báo cáo. EDW là kho tích hợp, không trùng lặp, được mô hình theo chủ đề (subject area).
Ưu điểm: tính nhất quán và toàn vẹn cao, tốt cho doanh nghiệp lớn có nhiều nguồn. Nhược: thời gian và chi phí ban đầu lớn, cần kiến trúc sư dữ liệu giỏi, ra giá trị chậm hơn.
Data Vault — Hub / Link / Satellite
Data Vault (Dan Linstedt) là phương pháp dành cho môi trường nhiều nguồn, thay đổi liên tục và yêu cầu kiểm toán nghiêm ngặt. Nó tách dữ liệu thành ba loại bảng:
- Hub: chứa khoá nghiệp vụ (business key) duy nhất của một thực thể, ví dụ Hub_KhachHang chỉ giữ CIF.
- Link: mô tả quan hệ giữa các hub, ví dụ Link giữa khách hàng và tài khoản.
- Satellite: chứa các thuộc tính mô tả và lịch sử thay đổi của hub hoặc link, kèm timestamp tải.
Triết lý cốt lõi: tách khoá nghiệp vụ (ổn định) khỏi thuộc tính (hay đổi) và quan hệ. Mọi thứ chỉ insert, không update/delete, nên giữ được toàn bộ lịch sử và dễ audit. Tải song song được vì các hub độc lập nhau.
Bảng so sánh
| Tiêu chí | Kimball | Inmon | Data Vault |
|---|---|---|---|
| Hướng tiếp cận | Bottom-up | Top-down | Bottom-up nguồn, raw-first |
| Mô hình cốt lõi | Star schema (denormalized) | EDW 3NF chuẩn hoá | Hub / Link / Satellite |
| Đối tượng dùng trực tiếp | Người nghiệp vụ | Engineer rồi mới derive | Engineer (cần lớp mart phía trên) |
| Tốc độ ra giá trị | Nhanh | Chậm | Trung bình |
| Khả năng audit / lịch sử | Trung bình (qua SCD) | Khá | Rất mạnh (insert-only) |
| Chịu thay đổi nguồn | Trung bình | Yếu (refactor tốn) | Rất tốt (thêm sat/link) |
| Độ phức tạp | Thấp | Cao | Cao (nhiều bảng) |
| Phù hợp khi | Mart phân tích, BI nhanh | EDW tập trung ổn định | Đa nguồn, quy định ngặt |
Thực tế nhiều ngân hàng dùng kiến trúc lai: Data Vault hoặc một raw/staging layer ở tầng dưới để gom và lưu lịch sử mọi nguồn, rồi dựng các star schema (Kimball) ở tầng presentation cho BI. Đây cũng là tinh thần của kiến trúc medallion (bronze/silver/gold).
Các loại Fact Table
Chọn đúng loại fact quan trọng ngang với chọn grain. Có ba loại chính.
Transaction fact
Mỗi dòng là một sự kiện xảy ra tại một thời điểm — chi tiết nhất, chỉ insert thêm. Ví dụ ngân hàng: fact giao dịch, mỗi dòng là một lần chuyển tiền/rút/nạp. Bảng này lớn nhanh nhưng cho phép phân tích đến từng giao dịch.
Periodic snapshot fact
Mỗi dòng là trạng thái đo được theo một chu kỳ cố định (cuối ngày, cuối tháng), kể cả khi không có giao dịch nào. Ví dụ kinh điển ngân hàng: snapshot số dư cuối ngày — mỗi tài khoản mỗi ngày một dòng với số dư cuối ngày. Bạn không thể tính tổng số dư toàn hệ thống bằng cách cộng các giao dịch một cách rẻ tiền; snapshot làm việc này trực tiếp và nhanh. Đây là dạng fact "semi-additive": số dư cộng được theo tài khoản nhưng không cộng được theo thời gian (cộng số dư của 30 ngày lại không ra gì có nghĩa — phải lấy trung bình hoặc giá trị cuối kỳ).
Accumulating snapshot fact
Mỗi dòng theo dõi vòng đời của một thực thể qua nhiều mốc, và dòng đó được update khi mốc mới đạt được. Ví dụ: hồ sơ vay với các cột mốc ngày nộp, ngày thẩm định, ngày phê duyệt, ngày giải ngân. Khi hồ sơ chuyển trạng thái, dòng tương ứng được cập nhật. Loại này tối ưu để đo thời lượng giữa các bước (lead time).
| Loại fact | Mỗi dòng là | Thao tác | Tính cộng | Ví dụ ngân hàng |
|---|---|---|---|---|
| Transaction | Một sự kiện | Insert | Fully additive (số tiền) | Giao dịch chuyển tiền |
| Periodic snapshot | Trạng thái cuối kỳ | Insert | Semi-additive (số dư) | Số dư cuối ngày |
| Accumulating snapshot | Vòng đời thực thể | Insert + Update | Phụ thuộc cột | Tiến trình hồ sơ vay |
Grain — khai báo trước, mọi thứ sau
Grain là mức chi tiết của một dòng fact — "một dòng tương ứng với cái gì". Quy tắc vàng của Kimball: khai báo grain trước khi chọn dimension hay measure. Mọi dimension phải đúng với grain đó, mọi measure phải nhất quán với grain đó.
Ví dụ khai báo grain rõ ràng cho fact giao dịch: "một dòng cho mỗi lần ghi nợ/ghi có trên một tài khoản tại một thời điểm". Với câu này ta biết ngay khách hàng, tài khoản, ngày, kênh là dimension hợp lệ, còn số tiền và phí là measure. Nếu grain mơ hồ ("một dòng cho mỗi giao dịch") người ta dễ trộn lẫn mức giao dịch tổng và mức bút toán chi tiết, gây nhân đôi số liệu khi join.
Nguyên tắc: chọn grain chi tiết nhất có thể mà nguồn cho phép. Có thể tổng hợp lên (aggregate) từ chi tiết, nhưng không thể tách ngược từ tổng.
Surrogate key vs natural key
Natural key (khoá tự nhiên / business key) là định danh đến từ nghiệp vụ: CIF của khách hàng, số tài khoản, mã giao dịch. Surrogate key là khoá nhân tạo do warehouse sinh ra, thường là số nguyên tăng dần (hoặc hash), không mang ý nghĩa nghiệp vụ.
Dimension nên dùng surrogate key làm primary key, vì:
- Bắt buộc cho SCD Type 2: cùng một khách hàng (cùng CIF) có thể tồn tại nhiều phiên bản lịch sử, mỗi phiên bản cần một surrogate key riêng để fact trỏ đúng phiên bản tại thời điểm sự kiện.
- Cách ly khỏi thay đổi nguồn: nếu nguồn đổi định dạng CIF hoặc gộp hai hệ thống, warehouse không bị vỡ.
- Hiệu năng: join trên số nguyên nhanh và gọn hơn chuỗi.
- Xử lý "unknown": gán surrogate key đặc biệt (ví dụ -1) cho các giá trị chưa biết/chưa khớp.
Natural key vẫn được lưu trong dimension như một thuộc tính để truy vết về nguồn.
Slowly Changing Dimensions (SCD)
Dimension thay đổi theo thời gian (khách hàng đổi địa chỉ, đổi phân khúc). Cách ta xử lý thay đổi đó quyết định warehouse "nhớ" lịch sử như thế nào.
| Type | Cách xử lý | Giữ lịch sử? | Khi dùng |
|---|---|---|---|
| 0 | Cố định, không bao giờ đổi | Không (giữ giá trị gốc) | Ngày mở tài khoản, CIF |
| 1 | Ghi đè giá trị cũ | Không | Sửa lỗi chính tả, thuộc tính không cần lịch sử |
| 2 | Thêm dòng mới + valid_from/valid_to + current_flag | Đầy đủ | Phân khúc, địa chỉ — cần lịch sử |
| 3 | Thêm cột "giá trị trước" | Một bước trước | Khi chỉ cần so với trạng thái cũ gần nhất |
| 4 | Tách bảng history riêng | Đầy đủ (bảng phụ) | Thuộc tính đổi rất thường xuyên |
| 6 | Kết hợp 1+2+3 (hybrid) | Đầy đủ + truy cập nhanh giá trị hiện tại | Cần cả lịch sử lẫn báo cáo theo giá trị hiện tại |
Type 2 chi tiết — xương sống của warehouse có lịch sử
Type 2 là kiểu quan trọng nhất. Mỗi khi thuộc tính theo dõi thay đổi, ta đóng dòng cũ (set valid_to và current_flag = false) và mở một dòng mới (surrogate key mới, valid_from = thời điểm thay đổi, valid_to để mở/9999-12-31, current_flag = true).
-- (minh hoạ, không chạy trong sandbox)
CREATE TABLE dim_khach_hang (
khach_hang_sk BIGINT PRIMARY KEY, -- surrogate key
cif VARCHAR(20) NOT NULL, -- natural key
ho_ten VARCHAR(200),
phan_khuc VARCHAR(50), -- thuoc tinh theo doi (Type 2)
valid_from TIMESTAMP NOT NULL,
valid_to TIMESTAMP NOT NULL, -- 9999-12-31 neu dang hieu luc
current_flag BOOLEAN NOT NULL
);
-- Khi khach hang doi phan khuc: dong dong cu
UPDATE dim_khach_hang
SET valid_to = CURRENT_TIMESTAMP, current_flag = FALSE
WHERE cif = 'C001' AND current_flag = TRUE;
-- Mo dong moi
INSERT INTO dim_khach_hang
VALUES (10245, 'C001', 'Nguyen Van A', 'Priority',
CURRENT_TIMESTAMP, TIMESTAMP '9999-12-31 00:00:00', TRUE);
Lợi ích: một giao dịch xảy ra hồi tháng 3 sẽ trỏ tới surrogate key của phiên bản khách hàng tại tháng 3 (phân khúc cũ), nên báo cáo lịch sử phản ánh đúng bối cảnh khi đó — đây là "as-was reporting". Khi cần báo cáo theo trạng thái hiện tại, lọc current_flag = TRUE.
Các type còn lại
- Type 0 giữ nguyên giá trị gốc — phù hợp thuộc tính bất biến như ngày mở tài khoản.
- Type 1 ghi đè, mất lịch sử — dùng để sửa lỗi dữ liệu hoặc khi nghiệp vụ không quan tâm giá trị cũ.
- Type 3 thêm cột như
phan_khuc_truoc_do— chỉ nhớ một bước, hiếm khi đủ. - Type 4 tách bảng
dim_khach_hang_historychứa mọi phiên bản, còn bảng chính chỉ giữ phiên bản hiện tại — tốt khi thuộc tính đổi cực nhanh. - Type 6 kết hợp: dòng có cả
phan_khuc(giá trị hiện tại, Type 1) lẫnphan_khuc_tai_thoi_diem(Type 2) lẫn cột "trước đó" (Type 3), cho phép báo cáo cả as-was lẫn as-is mà không phải tự join lại.
Các kiểu dimension đặc biệt
Degenerate dimension
Là một định danh nghiệp vụ nằm ngay trong fact table mà không cần bảng dimension riêng, vì nó không có thuộc tính mô tả nào khác. Ví dụ mã giao dịch hay số phiếu chuyển khoản lưu thẳng trong fact giao dịch — dùng để gom các bút toán cùng một giao dịch hoặc truy vết.
Junk dimension
Gom nhiều cờ/trạng thái lặt vặt (yes/no, loại nhỏ) rải rác vào một bảng dimension duy nhất chứa các tổ hợp giá trị, thay vì để chúng làm phình fact table bằng nhiều cột boolean. Ví dụ ngân hàng: tổ hợp (kênh online/offline, có khuyến mãi hay không, giao dịch nội/ngoại tệ) gom vào dim_giao_dich_flags.
Role-playing dimension
Một dimension được dùng nhiều vai trò khác nhau trong cùng một fact. Kinh điển là dim_ngay: trong fact hồ sơ vay nó đóng vai ngày nộp, ngày phê duyệt, ngày giải ngân. Ta tạo view/alias cho mỗi vai để báo cáo rõ ràng (date_nop, date_phe_duyet...).
Bridge table cho quan hệ n-n
Khi quan hệ giữa fact và dimension là nhiều-nhiều, ta chèn một bridge table. Ví dụ một tài khoản có nhiều chủ sở hữu (joint account), hoặc một giao dịch gắn nhiều phân loại. Bridge thường mang thêm trọng số phân bổ (allocation weight) để tránh nhân đôi số liệu khi nhiều chủ cùng "nhận" một số dư.
Late arriving data
Dữ liệu đến trễ là chuyện thường ngày: một giao dịch ghi nhận muộn, hoặc thông tin dimension đến sau khi fact tham chiếu nó đã tới.
- Late arriving fact: sự kiện cũ về muộn. Khi đó phải gắn nó với phiên bản dimension đúng tại thời điểm sự kiện (dùng valid_from/valid_to để tìm surrogate key đúng), không phải phiên bản hiện tại.
- Late arriving dimension (early arriving fact): fact đến nhưng khoá dimension chưa có trong dim. Cách xử lý phổ biến là tạo một dòng dimension "inferred" với surrogate key mới và các thuộc tính rỗng/unknown, để fact trỏ vào; khi dữ liệu dimension thật đến, ta cập nhật (Type 1) hoặc đóng/mở dòng (Type 2) cho khớp. Nếu không, fact buộc phải trỏ tới surrogate key "unknown" (-1).
Thiết kế warehouse tốt phải tính trước những trường hợp này — đặc biệt trong ngân hàng nơi đối soát (reconciliation) cuối ngày thường kéo theo các bản ghi điều chỉnh đến trễ.
Tóm tắt
Mô hình hoá kho dữ liệu vẫn là kỹ năng cốt lõi dù phần cứng đã rẻ. Dimensional modeling (fact + dimension, star schema) là nền tảng cho BI. Ba trường phái — Kimball (bottom-up, star, conformed dimension), Inmon (top-down, EDW 3NF), Data Vault (hub/link/satellite, insert-only, audit mạnh) — phục vụ các nhu cầu khác nhau, và kiến trúc lai (Vault dưới, star trên) rất phổ biến trong ngân hàng. Có ba loại fact: transaction (giao dịch), periodic snapshot (số dư cuối ngày, semi-additive) và accumulating snapshot (vòng đời hồ sơ vay). Luôn khai báo grain trước, dùng surrogate key cho dimension, và xử lý lịch sử bằng SCD — đặc biệt Type 2 với valid_from/valid_to và current_flag. Cuối cùng, đừng quên các dimension đặc biệt (degenerate, junk, role-playing), bridge table cho quan hệ n-n, và chiến lược cho dữ liệu đến trễ.
Tự kiểm tra
- Khác biệt cốt lõi giữa Kimball và Inmon là gì, và vì sao Data Vault lại mạnh về audit?
- Cho fact "số dư cuối ngày", vì sao nó là semi-additive và bạn không nên cộng số dư theo trục thời gian?
- Tại sao SCD Type 2 bắt buộc phải dùng surrogate key chứ không thể chỉ dùng natural key?
- Khi một giao dịch tháng 3 đến trễ vào tháng 6, làm sao để nó trỏ đúng phiên bản dimension khách hàng?
- Khi nào bạn chọn junk dimension thay vì để các cờ trạng thái nằm trong fact table?
- Bridge table giải quyết vấn đề gì với tài khoản đồng sở hữu, và vai trò của cột trọng số phân bổ?
Đọc tiếp
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.