Data Modeling 6 — Mô hình hiện đại: OBT, columnar & semantic layer
Vì sao tư duy mô hình hoá phải đổi
Các mô hình kinh điển — chuẩn hoá 3NF, sao (star) và bông tuyết (snowflake) của Kimball — ra đời trong thời kỳ mà lưu trữ đắt và CPU/IO là nút thắt. Trên các cơ sở dữ liệu hàng (row-oriented) đời cũ, dư thừa dữ liệu tốn tiền ổ đĩa thật sự, còn quét toàn bảng rất chậm. Chuẩn hoá để tránh lặp, rồi ghép (join) lại khi cần, là một đánh đổi hợp lý: tiết kiệm chỗ, đổi lấy chi phí join.
Thời cloud/columnar đảo ngược nhiều giả định đó. Các kho phân tích serverless, lưu theo cột như BigQuery hay Snowflake (bài này lấy BigQuery làm ví dụ tham chiếu) tách rời lưu trữ khỏi tính toán. Lưu trữ trở nên rất rẻ, và động cơ được thiết kế để quét cột cực nhanh: chỉ đọc đúng những cột truy vấn cần, nén rất tốt nhờ các kỹ thuật như run-length encoding (RLE) và dictionary encoding. Một cột lặp lại một giá trị hàng nghìn lần không bị lưu nghìn lần — nó được lưu một lần cùng thông tin "lặp bao nhiêu dòng". Hệ quả: hình phạt cho phi chuẩn hoá (denormalization) trên kho cột nhỏ hơn nhiều so với hệ thống hàng.
Nhưng có một điều không rẻ đi: join phân tán vẫn tốn. Ghép hai bảng lớn trên hệ phân tán thường phải xáo trộn (shuffle) dữ liệu qua mạng giữa các node — đúng thứ chi phí đắt nhất trong xử lý phân tán (xem Spark shuffle ở khía cạnh lakehouse). Tổng kết lại một câu để giữ trong đầu suốt bài:
Storage rẻ đi, quét cột nhanh lên, nhưng join lớn vẫn đắt. Đó chính là nền tảng kinh tế cho các mô hình phi chuẩn hoá kiểu One Big Table.
One Big Table (OBT) / wide table
One Big Table (OBT), còn gọi là wide table hay bảng phẳng rộng, là kỹ thuật gộp một bảng fact với toàn bộ các thuộc tính chiều (dimension) liên quan vào một bảng duy nhất, đã "phẳng hoá" sẵn — không còn khoá ngoại trỏ sang bảng dim, không cần join lúc truy vấn. Thay vì fct_orders trỏ tới dim_customer, dim_product, dim_store, ta có một bảng obt_orders chứa luôn customer_name, customer_segment, product_category, store_region... ngay trên mỗi dòng đơn hàng.
Ưu điểm:
- Không join khi đọc. Đây là lợi ích lớn nhất. Loại bỏ hoàn toàn join phân tán lúc truy vấn — thứ đắt đỏ nhất — nên đọc rất nhanh. Trên kho cột, các so sánh thực tế cho thấy bảng phi chuẩn hoá thường nhanh hơn star schema đáng kể ở khối lượng lớn (mức cải thiện tuỳ nền tảng và mẫu truy vấn).
- Đơn giản cho người dùng cuối. Analyst chỉ cần
SELECT ... FROM obtvớiWHERE/GROUP BY, không phải nhớ sơ đồ join giữa nhiều bảng. Dễ cắm vào công cụ BI, dễ tạo feature cho ML (mỗi dòng đã là một vector đặc trưng đầy đủ). - Tối ưu cho quét cột. Chỉ những cột được dùng mới bị đọc; các cột thừa không làm chậm truy vấn vì columnar bỏ qua chúng.
Nhược điểm:
- Dư thừa dữ liệu. Thông tin chiều lặp lại trên mọi dòng fact: một khách hàng có 100 đơn thì tên, email, phân khúc của họ được nhân bản 100 lần. Trên columnar chi phí lưu trữ được nén tốt, nhưng dư thừa logic vẫn còn.
- Cập nhật chiều khó. Khi một thuộc tính chiều đổi (khách đổi email), ta phải sửa hàng nghìn dòng thay vì một dòng trong bảng dim. Việc xử lý lịch sử thay đổi (SCD) và tính đúng đắn trở nên phức tạp hơn.
- Bảng phình và cứng. Thêm một thuộc tính chiều mới trên star chỉ là thêm cột vào bảng dim nhỏ; trên OBT là sửa một bảng khổng lồ và backfill lại lịch sử — tốn tài nguyên. Bảng có thể phình tới hàng trăm cột, khó bảo trì.
Khi nào nên dùng OBT:
- Bảng phục vụ feature ML — mỗi dòng cần đầy đủ đặc trưng, không muốn join lúc training/serving.
- Bảng serving dashboard nóng — truy vấn lặp đi lặp lại với độ trễ thấp, ưu tiên tốc độ đọc hơn tính linh hoạt.
- Dữ liệu append-mostly (chủ yếu thêm, ít sửa) — event log, giao dịch đã chốt — nơi vấn đề cập nhật chiều ít nghiêm trọng.
Star vs OBT trên kho cột
Đừng hiểu OBT là "star đã lỗi thời". Trên columnar, star schema vẫn rất tốt và dễ bảo trì:
- Cập nhật chiều gọn — sửa một dòng dim thay vì hàng nghìn dòng fact; xử lý SCD (xem Fact & Dimension tables) sạch sẽ.
- Chống dư thừa và mâu thuẫn — mỗi thuộc tính chiều có một nơi duy nhất.
- Công cụ BI ưa star. Looker và Power BI mô hình hoá quan hệ theo star rất tự nhiên; nhiều tối ưu của chúng giả định cấu trúc fact–dimension.
Cách làm phổ biến của các đội trưởng thành là kết hợp, không giáo điều: giữ star schema ở tầng marts làm mô hình chuẩn, dễ bảo trì, dễ tái dùng; rồi vật chất hoá (materialize) thêm OBT cho những use case cụ thể cần tốc độ hoặc feature ML — OBT được build từ star bằng một model dbt, coi như một "cache phi chuẩn hoá" phía dưới. Star là nguồn, OBT là dạng phục vụ.
Riêng BigQuery còn có một dạng phi chuẩn hoá trung gian tinh tế: nested & repeated fields qua kiểu ARRAY và STRUCT (xem BigQuery SQL features). Thay vì tách order và order_items thành hai bảng rồi join, ta lồng các line_items thành một ARRAY<STRUCT<...>> ngay trong dòng order. Đây là "denormalize không dư thừa": dữ liệu con nằm cùng dòng cha (không lặp thuộc tính cha), truy vấn không cần join, chỉ cần UNNEST khi muốn dàn phẳng. Đó là điểm trung dung rất mạnh giữa star và OBT trên BigQuery.
Kiến trúc Medallion (bronze / silver / gold)
Song song với câu hỏi "bảng hình gì" là câu hỏi "tổ chức các tầng ra sao". Medallion architecture (kiến trúc huy chương) là mẫu tổ chức pipeline theo ba tầng chất lượng tăng dần:
- Bronze (đồng) — raw. Nạp dữ liệu từ hệ nguồn nguyên trạng (as-is), cấu trúc bám sát bảng nguồn, thêm vài cột metadata (thời điểm nạp, id lần chạy, nguồn). Bronze là "bản sao lịch sử bất biến" — không sửa, chỉ thêm; là nơi để tái xử lý khi logic phía sau đổi.
- Silver (bạc) — cleaned / conformed. Làm sạch, khử trùng lặp, ép kiểu, chuẩn hoá, khớp và hợp nhất để cho ra "góc nhìn doanh nghiệp" về các thực thể lõi: khách hàng chuẩn, giao dịch không trùng, bảng tham chiếu chéo. Đây là tầng dữ liệu đáng tin để nhiều use case dùng chung.
- Gold (vàng) — aggregated / serving. Mô hình hướng nghiệp vụ, đọc-tối-ưu, ít join hơn: các bảng tổng hợp, star schema marts hoặc OBT phục vụ báo cáo và phân tích. Đây là tầng người dùng cuối và BI chạm vào.
Điểm mấu chốt: medallion ánh xạ gần như 1-1 với cấu trúc tầng của dbt — chỉ khác tên gọi:
| Medallion | dbt | Vai trò |
|---|---|---|
| Bronze | sources / raw | dữ liệu thô, chưa biến đổi |
| Silver | staging → intermediate | chuẩn hoá nhẹ, làm sạch, hợp nhất |
| Gold | marts | fact/dim (Kimball) hoặc OBT phục vụ |
Trong lakehouse (Delta/Iceberg trên Spark — xem Delta lakehouse), medallion là mẫu tổ chức mặc định. Trên warehouse thuần SQL, dbt hiện thực đúng ba tầng đó. Nói cho đúng: đây không phải phát minh mới — các đội đã build pipeline "staging → intermediate → mart" nhiều năm; medallion chỉ đặt cho nó một cái tên dễ nhớ và một bộ quy tắc rõ ràng.
Semantic layer / metrics layer
Ngay cả khi tầng gold sạch đẹp, vẫn còn một lỗ hổng kinh điển: "doanh thu" là gì? Có tính thuế không? Trừ hoàn tiền chưa? Tính theo ngày đặt hay ngày giao? Nếu mỗi báo cáo tự viết SQL trả lời câu này, ta sẽ có năm con số khác nhau cho cùng một chỉ tiêu — và những cuộc họp cãi nhau "số của ai đúng".
Semantic layer / metrics layer giải quyết đúng vấn đề đó: định nghĩa mỗi metric MỘT LẦN, tập trung, để mọi dashboard, notebook, chart nhúng, thậm chí AI agent hỏi cùng câu đều nhận cùng một đáp án. Nó là nguồn sự thật duy nhất (single source of truth) về chỉ số, đặt phía trên tầng gold. Các cài đặt tiêu biểu:
- dbt Semantic Layer (MetricFlow). Định nghĩa metric ngay trong dbt project — cạnh chính các model tạo ra dữ liệu. Downstream (Tableau, Power BI, Hex, Mode, Sigma, Lightdash) truy vấn metric qua API/JDBC, luôn nhất quán.
- Looker LookML. Ngôn ngữ mô hình hoá ngữ nghĩa tích hợp trong Looker, trưởng thành từ 2014 — bản mẫu của tư tưởng "định nghĩa metric sống cạnh công cụ BI".
- Cube — headless BI. Tách hẳn tầng ngữ nghĩa khỏi công cụ trực quan hoá, phơi metric qua REST / GraphQL / SQL API để bất kỳ consumer nào (BI, notebook, app nhúng, agent) dùng chung một định nghĩa.
Khái niệm headless BI chính là kiến trúc này: tách logic nghiệp vụ và governance ra khỏi mọi công cụ hiển thị cụ thể. Metric được phơi qua API chuẩn; công cụ nào cũng chỉ là một "cái đầu" hiển thị cắm vào cùng một "thân" ngữ nghĩa. Lợi ích: đổi công cụ BI không phải viết lại định nghĩa metric; và trong thời AI, một agent hỏi "doanh thu tháng trước" đi qua đúng semantic layer sẽ trả về con số đã được kiểm định thay vì tự bịa SQL.
ELT & mô hình hoá theo tầng
Semantic layer và medallion đều dựa trên một chuyển dịch nền tảng: ELT thay ETL. Thay vì biến đổi ở một cụm xử lý ngoài rồi mới nạp (ETL), ta nạp dữ liệu thô thẳng vào kho rồi transform ngay trong kho bằng SQL — thường qua dbt. Kho cột hiện đại đủ mạnh và rẻ để tự chạy các phép biến đổi khổng lồ, nên tầng transform riêng bên ngoài không còn cần thiết.
Trong khuôn khổ ELT, mô hình hoá diễn ra theo tầng model của dbt:
- staging — chuẩn hoá nhẹ: đổi tên cột về convention, ép kiểu, làm sạch cơ bản; mỗi model staging ứng một bảng nguồn (tương ứng silver sớm).
- intermediate — bước trung gian: hợp nhất, tính toán tái dùng, tách logic phức tạp thành mảnh nhỏ.
- marts — sản phẩm cuối hướng nghiệp vụ: có thể là Kimball star (
fct_,dim_) hoặc OBT tuỳ use case, hoặc cả hai (star làm chuẩn + OBT phục vụ tốc độ).
Data mesh & hướng domain (nhắc ngắn)
Khi tổ chức lớn lên, một đội trung tâm ôm mọi pipeline trở thành nút thắt. Data mesh đề xuất phi tập trung: mỗi domain nghiệp vụ (bán hàng, tín dụng, rủi ro...) sở hữu dữ liệu của mình như một data product — có chủ sở hữu, có hợp đồng giao diện, có cam kết chất lượng và SLA. Semantic layer và tiêu chuẩn chung đảm bảo các data product vẫn ghép nối và nhất quán được với nhau. Chi tiết về ownership, hợp đồng dữ liệu và kiểm soát chất lượng thuộc về Governance & Data Quality.
Ví dụ minh hoạ: OBT vs Star
Cùng một câu hỏi "doanh thu theo phân khúc khách và danh mục sản phẩm", hai cách viết. Sandbox học tập ở đây là PostgreSQL, nên các ví dụ chỉ để minh hoạ cấu trúc và ý tưởng; trên kho cột thật (BigQuery) khác biệt hiệu năng mới rõ.
Cách star schema — phải join fact với các dim:
-- (minh hoạ) Star: join fact với dimension khi truy vấn
SELECT
d_cust.customer_segment,
d_prod.product_category,
SUM(f.amount) AS revenue
FROM fct_orders f
JOIN dim_customer d_cust ON d_cust.customer_key = f.customer_key
JOIN dim_product d_prod ON d_prod.product_key = f.product_key
GROUP BY 1, 2;
Cách OBT — thuộc tính chiều đã phẳng sẵn trên fact, không join:
-- (minh hoạ) OBT: mọi thuộc tính đã có sẵn trên một bảng phẳng
SELECT
customer_segment,
product_category,
SUM(amount) AS revenue
FROM obt_orders
GROUP BY 1, 2;
Và bảng OBT thường được build từ star bằng một model dbt — star là nguồn, OBT là dạng phục vụ:
-- (minh hoạ) model dbt xây OBT bằng cách phẳng hoá star một lần
-- models/marts/obt_orders.sql (materialized = table)
SELECT
f.order_id,
f.order_date,
f.amount,
d_cust.customer_segment,
d_cust.customer_region,
d_prod.product_category,
d_prod.product_brand
FROM {{ ref('fct_orders') }} f
JOIN {{ ref('dim_customer') }} d_cust ON d_cust.customer_key = f.customer_key
JOIN {{ ref('dim_product') }} d_prod ON d_prod.product_key = f.product_key
Trên BigQuery, một biến thể trung dung là lồng dòng con bằng ARRAY<STRUCT> thay vì tách bảng con:
(minh hoạ) một dòng order với line_items lồng bên trong (BigQuery nested/repeated):
order_id | order_date | customer_segment | line_items (ARRAY<STRUCT>)
---------+-------------+------------------+------------------------------------------
1001 | 2026-06-30 | premium | [ {sku:'A', qty:2, price:50},
| | | {sku:'B', qty:1, price:30} ]
-- truy vấn dàn phẳng khi cần, không cần join bảng riêng:
SELECT order_id, item.sku, item.qty
FROM obt_orders, UNNEST(line_items) AS item;
Use case thực tế
1) OBT phục vụ dashboard nóng. Một dashboard doanh thu realtime bị nhiều người mở liên tục, mỗi lần lại join 4–5 bảng dim thì chậm và tốn slot/compute. Giải pháp: giữ star ở marts làm chuẩn, rồi build thêm obt_sales_daily (đã phẳng, đã tổng hợp theo ngày × phân khúc × vùng) và cho dashboard trỏ vào đó. Truy vấn không join, độ trễ thấp, chi phí quét ổn định. OBT được refresh theo lịch từ star, nên định nghĩa nghiệp vụ vẫn nằm một nơi.
2) OBT làm feature store cho ML. Đội mô hình cần mỗi khách hàng là một dòng với đầy đủ đặc trưng (tổng chi tiêu 30/90 ngày, số giao dịch, phân khúc, tỉnh...). Build một OBT feature_customer phẳng, mỗi dòng một vector đặc trưng — dùng trực tiếp cho training và serving, không phải join lúc infer. Đây là nơi OBT toả sáng: dư thừa không phải vấn đề vì dữ liệu chủ yếu append và đọc.
3) Metrics layer làm SSoT chỉ số. Ngân hàng có "tỷ lệ nợ xấu (NPL)" được ban giám đốc, phòng rủi ro và báo cáo cơ quan quản lý dùng — nhưng ba nơi từng tính ba kiểu (mẫu số khác nhau, mốc thời gian khác nhau). Đưa định nghĩa NPL vào semantic layer (dbt Semantic Layer hoặc LookML): một công thức duy nhất, một cách chọn tử số/mẫu số. Từ đó Power BI, Looker và cả agent hỏi tự nhiên đều trả về cùng một con số — hết cãi nhau "số của ai đúng".
Ghi nhớ
- Kinh tế đổi → mô hình đổi: storage rẻ, quét cột nhanh, nhưng join lớn vẫn đắt — đó là lý do OBT/denormalize hấp dẫn trên kho cột.
- OBT đổi dư thừa + khó cập nhật chiều lấy đọc cực nhanh (không join) và đơn giản; hợp cho feature ML và dashboard nóng, dữ liệu append-mostly.
- Star vẫn sống khoẻ trên columnar: dễ bảo trì, gọn khi cập nhật chiều, và được BI (Looker/Power BI) ưa. Cách khôn ngoan: star ở marts + OBT cho use case cụ thể, build OBT từ star.
- BigQuery nested/repeated (
ARRAY/STRUCT) là denormalize không dư thừa — trung dung giữa star và OBT. - Medallion (bronze→silver→gold) ánh xạ gần 1-1 với sources→staging/intermediate→marts của dbt; là cách tổ chức tầng chất lượng, không phải phát minh mới.
- Semantic/metrics layer = định nghĩa metric một lần, dùng nhất quán khắp nơi (dbt SL/MetricFlow, LookML, Cube) → single source of truth; headless BI tách logic khỏi công cụ hiển thị.
- Không giáo điều: kết hợp Kimball / OBT / Data Vault theo nhu cầu truy vấn, tần suất cập nhật, khối lượng và chi phí trên nền tảng của bạn.
Bài trước trong series: Kimball & mô hình chiều, Fact & Dimension tables. Bài tiếp: Governance & Data Quality. Liên quan: BigQuery SQL features, dbt tổng quan, Looker tổng quan, Delta lakehouse.
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.