返回文章列表

模組化資料模型設計與最佳實踐

本文探討模組化資料模型設計的關鍵原則與最佳實踐,包含分解、抽象化、重用性等核心概念,並輔以 dbt 工具的實務應用,涵蓋 staging、中間層、mart 等不同層級的模型建構,同時強調資料模型測試與檔案生成的重要性,以提升資料管道的可維護性、可擴充套件性和團隊協作效率。

資料工程 資料函式庫

模組化資料模型設計在現代資料工程中扮演著至關重要的角色,尤其在大型專案中,它能有效簡化複雜資料結構的管理和操控。本文探討了模組化資料建模的核心原則,包含分解、抽象化和重用性,並以 dbt 工具為例,展示如何實踐這些原則。文章涵蓋了從原始資料到 staging 模型、中間層模型以及最終 mart 模型的完整流程,並提供程式碼範例說明如何使用 dbt 建立和管理這些模型。此外,本文也強調了測試和檔案生成的重要性,以確保資料模型的準確性、可靠性和可理解性,最終提升整體資料管道的效率和可維護性。

建構模組化資料模型的關鍵原則

在大型專案中,眾多開發人員需要分享程式碼或與彼此的程式碼介接時,模組化變得至關重要。模組化使得複雜軟體的開發變得可靠。在軟體工程領域,模組化是理所當然的,但在資料領域,直到近年才開始被重視。

模組化的優勢

模組化簡化了編碼過程,同樣也能簡化資料模型的設計和開發。透過將複雜的資料結構分解為模組化元件,資料工程師能夠更好地管理和操控不同粒度的資料。這種模組化方法提高了資料整合的效率、可擴充套件性和靈活性,使得更新、維護和增強整體資料架構變得更容易。

模組化資料建模的核心原則

  1. 分解:將資料模型分解為更小、更易於管理的元件。
  2. 抽象化:在介面後隱藏資料模型的實作細節。
  3. 重用性:建立可在系統的多個部分中重複使用的元件。

實作模組化資料建模

這種資料建模可以透過正規化、資料倉儲和資料虛擬化技術來實作。例如,使用正規化技術,根據資料的特性和關係將其分離到不同的表中,從而形成模組化的資料模型。

另一個選擇是利用 dbt 工具,因為它能夠自動化建立模組化資料模型的過程,提供多項支援模組化資料建模原則的功能。例如,dbt 允許將資料模型分解為更小的可重複使用元件,提供了一種建立可重複使用的巨集和模組化模型檔案的方法。

使用 dbt 實作模組化的範例程式碼

-- macros/my_macro.sql
{% macro generate_date_spine(start_date, end_date) %}
  {{
    dbt_utils.date_spine(
      datepart="day",
      start_date=start_date,
      end_date=end_date
    )
  }}
{% endmacro %}

內容解密:

這段程式碼定義了一個名為 generate_date_spine 的巨集,用於生成一個日期範圍內的日期脊柱(date spine)。dbt_utils.date_spine 是 dbt 提供的一個實用函式,用於生成一系列連續的日期。

dbt 在模組化資料建模中的作用

dbt 鼓勵重用性,提供了一種方法來定義和重複使用各種模型中的通用程式碼。此外,dbt 透過提供測試和記錄資料模型的方法來提高可維護性。最後,dbt 允許透過定義和測試不同的物化策略來最佳化效能,從而可以微調資料模型中各個元件的效能。

模組化的挑戰與風險

儘管模組化帶來了許多好處,但也存在潛在的缺點和風險。整合系統通常可以比模組化系統更好地最佳化,因為它可以最小化資料移動和記憶體使用,或者允許資料函式庫最佳化器在背後改進 SQL。

此圖示呈現了模組化資料建模的概念
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 模組化的挑戰與風險

rectangle "正規化" as node1
rectangle "dbt" as node2
rectangle "抽象化" as node3
rectangle "重用性" as node4

node1 --> node2
node2 --> node3
node3 --> node4

@enduml

圖表翻譯:

此圖示說明瞭從原始資料到模組化資料模型的過程。首先,透過正規化技術將原始資料轉換為模組化的結構。然後,利用 dbt 工具進一步將模組化資料模型分解為可重複使用的元件。這些元件透過抽象化簡化了資料介面,最終提高了整個系統的可維護性。

建構模組化資料模型

如同圖2-5所示,使用基礎資料模型跨越多個例項,而不是每次從頭開始,可以簡化資料建模中DAG的視覺化。這種模組化的多層結構闡明瞭資料建模邏輯的層次如何相互構建,並顯示了依賴關係。然而,必須注意的是,簡單地採用像dbt這樣的資料建模框架並不能自動確保模組化資料模型和易於理解的DAG。

圖表翻譯:

此圖示展示了dbt的模組化結構,說明瞭如何透過多層結構簡化資料建模。

DAG的結構取決於團隊的資料建模理念和思維過程,以及它們的一致性表達。為了實作模組化資料建模,可以考慮諸如命名約定、可讀性和易於除錯和最佳化等原則。這些原則可以應用於dbt中的各種模型,包括staging模型、中間模型和mart模型,以提高模組化並保持良好的DAG結構。

讓我們開始這段利用dbt進行模組化資料模型的旅程,首先了解dbt如何透過Jinja語法實作模型的重用性。

參照資料模型

透過採用dbt的功能,如模型參照和Jinja語法,資料工程師和分析師可以在模型之間建立清晰的依賴關係,提高程式碼的可重用性,並確保資料管道的一致性和準確性。Jinja是一種範本語言,允許在SQL程式碼中進行動態和程式化的轉換,為自定義和自動化資料轉換提供了強大的工具。這種模組化和dbt功能的強大組合使團隊能夠構建靈活和可維護的資料模型,加速開發過程並實作利益相關者之間的無縫協作。

使用ref()函式參照模型

要充分利用dbt的功能並確保模型的準確構建,必須使用{{ ref() }}語法進行模型參照。透過這種方式參照模型,dbt可以根據上游表自動檢測和建立模型之間的依賴關係。這使得資料轉換管道的執行順暢可靠。

另一方面,{{ source() }} Jinja語法應該謹慎使用,通常僅限於從資料函式庫中初始選擇原始資料。重要的是避免直接參照非dbt建立的表,因為它們會阻礙dbt工作流程的靈活性和模組化。相反,應該重點透過使用{{ ref() }} Jinja語法在模型之間建立關係,確保上游表的變化正確地傳播到下游,並保持清晰和連貫的資料轉換過程。透過堅持這些最佳實踐,dbt能夠實作高效的模型管理,並促進分析工作流程的可擴充套件性和可維護性。

-- 在orders.sql檔案中
SELECT
    o.order_id,
    o.order_date,
    o.order_amount,
    c.customer_name,
    c.customer_email
FROM
    {{ ref('orders') }} AS o
JOIN
    {{ ref('customers') }} AS c
ON
    o.customer_id = c.customer_id

-- 在customers.sql檔案中
SELECT
    customer_id,
    customer_name,
    customer_email
FROM
    raw_customers

內容解密:

此範例展示瞭如何在SQL查詢中使用ref()函式參照模型。場景涉及兩個模型檔案:orders.sqlcustomers.sql。在orders.sql檔案中,編寫了一個SELECT陳述式來檢索訂單資訊。{{ ref('orders') }}表示式參照了orders模型,允許查詢使用該模型中定義的資料。查詢透過customer_id列將orders模型與customers模型連線起來,檢索額外的客戶資訊,如姓名和電子郵件。在customers.sql檔案中,編寫了一個SELECT陳述式來提取客戶資訊。這個模型代表了原始客戶資料在任何轉換之前的狀態。

分層資料模型

Staging資料模型

Staging層在資料建模中扮演著至關重要的角色,因為它作為構建更複雜資料模型的基礎。每個staging模型都與原始資料來源有一對一的關係。保持staging模型的簡單性並最小化這一層中的轉換是非常重要的。可接受的轉換包括型別轉換、列重新命名、基本計算(如單位轉換)和使用條件陳述式(如CASE WHEN)進行分類別。

-- 簡單的staging模型範例
SELECT
    id AS customer_id,
    name AS customer_name,
    email AS customer_email
FROM
    {{ source('raw_data', 'customers') }}

內容解密:

這個範例展示了一個簡單的staging模型,它從原始資料來源中選擇客戶資訊,並進行列重新命名。這種方法保持了staging模型的簡單性,並為後續的資料轉換提供了基礎。

Staging模型通常以檢視的形式實作,以保持資料的及時性並最佳化儲存成本。這種方法允許參照staging層的中間或mart模型存取最新的資料,同時節省空間和成本。建議避免在staging層進行連線操作,以防止冗餘或重複的計算。連線操作更適合在後續層中建立更複雜的關係時進行。

此外,應避免在staging層進行聚合操作,因為它們可能會分組並可能限制對有價值的源資料的存取。Staging層的主要目的是為後續的資料模型建立基本的構建塊,提供下游轉換中的靈活性和可擴充套件性。遵循這些準則,staging層成為在模組化資料架構中構建強壯資料模型的可靠和高效的起點。

利用dbt中的staging模型使我們能夠在程式碼中採用Don’t Repeat Yourself(DRY)原則。透過遵循dbt的模組化和可重用結構,我們旨在建立一個乾淨、易於維護和分析的資料管道。

模組化資料建模的最佳實踐

在資料建模的過程中,模組化是一個非常重要的概念。透過將資料模型分解為不同的層級,我們可以提高資料處理的效率、降低複雜度,並增強資料的可讀性和可維護性。

基礎資料模型(Staging Models)

基礎資料模型的主要目的是對原始資料進行初步的轉換和準備,以便後續的處理。這些模型通常直接參照資料倉儲中的原始資料,並執行一些基本的轉換,如重新命名欄位或轉換資料型別。

stg_books 為例,我們可以建立一個 SQL 檔案 stg_books.sql 來定義這個基礎模型,如以下範例所示:

-- stg_books.sql
SELECT 
    book_id,
    title,
    author,
    publication_year,
    genre
FROM 
    raw_books

內容解密:

  • stg_books.sql 檔案查詢 raw_books 表格以建立新的模型。
  • 選擇相關的欄位,如 book_idtitleauthor 等。
  • 這個基礎模型負責初步的資料轉換和準備。

中間資料模型(Intermediate Models)

中間資料模型負責結合基礎模型中的原子構建塊,以建立更複雜和有意義的模型。這些模型通常代表對業務有意義的建構,但不直接暴露給終端使用者。

例如,我們可以建立一個中間模型 int_book_authors,結合 stg_booksstg_authors 的相關資訊。以下是一個範例:

-- int_book_authors.sql
WITH 
books AS (
    SELECT *
    FROM {{ ref('stg_books') }}
),
authors AS (
    SELECT *
    FROM {{ ref('stg_authors') }}
)
SELECT 
    b.book_id,
    b.title,
    a.author_id,
    a.author_name
FROM 
    books b
JOIN 
    authors a ON b.author_id = a.author_id

內容解密:

  • int_book_authors 模型參照了 stg_booksstg_authors 兩個基礎模型。
  • 使用 Jinja 語法 {{ ref() }} 確保 dbt 能正確推斷模型之間的依賴關係。
  • 結合兩個基礎模型的相關資訊,建立一個新的中間模型。

資料集市模型(Mart Models)

資料集市模型是資料管道的頂層,負責將業務定義的實體整合並呈現給終端使用者。這些模型通常物化為表格,以確保最佳的查詢效能。

圖表說明

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 圖表說明

rectangle "轉換" as node1
rectangle "整合" as node2
rectangle "呈現" as node3

node1 --> node2
node2 --> node3

@enduml

圖表翻譯: 此圖示呈現了資料從原始資料到最終的資料集市模型的流程。首先,原始資料經過轉換成為基礎資料模型;然後,這些基礎模型被整合到中間資料模型;最後,中間資料模型被呈現為最終的資料集市模型。

資料建模的最佳實踐:簡單性與測試的重要性

在建立資料分析的資料模型時,保持簡單性是至關重要的。過多的JOIN操作應該被避免。如果需要在資料模型中使用多個JOIN操作,則應該重新思考設計,並考慮重構中間層。透過保持資料模型的相對簡單,可以確保查詢執行的效率,並維護整個資料管道的效能。

簡單的資料模型範例:書籍出版分析

考慮一個用於書籍出版分析的資料集市。我們有一個名為int_book_authors的中間模型,其中包含原始的書籍資料,包括每本文的作者資訊(範例2-18)。

範例2-18:資料模型

-- mart_book_authors.sql
{{
  config(
    materialized='table',
    unique_key='author_id',
    sort='author_id'
  )
}}

WITH book_counts AS (
  SELECT
    author_id,
    COUNT(*) AS total_books
  FROM {{ ref('int_book_authors') }}
  GROUP BY author_id
)
SELECT
  author_id,
  total_books
FROM book_counts

內容解密:

  1. 設定模型組態:指定該模型應該被物化為一個表,unique_key設定為author_id以確保唯一性,並且根據author_id進行排序。
  2. 使用CTE聚合資料book_counts CTE從int_book_authors模型中選擇author_id並計算每個作者的書籍數量。
  3. 檢索聚合資料:最終的SELECT陳述式從book_counts CTE中檢索聚合資料,傳回每個作者的author_id和對應的書籍數量。

測試您的資料模型

在dbt中,測試是確保資料模型和資料來源準確性和可靠性的重要方面。dbt提供了一個全面的測試框架,允許您使用SQL查詢定義和執行測試。這些測試旨在識別不符合指定斷言標準的行或記錄,而不是檢查特定條件的正確性。

奇異測試範例

# tests.yml
version: 2
models:
  - name: my_model
    tests:
      - not_null_columns:
          columns:
            - column1
            - column2

內容解密:

  • 定義奇異測試:為my_model模型定義了一個名為not_null_columns的測試,用於檢查特定列是否包含NULL值。

通用測試範例

# generic_tests.yml
version: 2
tests:
  - name: non_negative_values
    severity: warn
    description: Check for non-negative values in specific columns
    columns:
      - column_name: amount
        assert_non_negative: {}
      - column_name: quantity
        assert_non_negative: {}

內容解密:

  • 定義通用測試:定義了一個名為non_negative_values的通用測試,用於檢查特定列的值是否為非負數。

重用通用測試

# my_model.yml
version: 2
models:
  - name: my_model
    columns:
      - column_name: amount
        tests: ["my_project.non_negative_values"]
      - column_name: quantity
        tests: ["my_project.non_negative_values"]

內容解密:

  • 重用通用測試:在my_model模型中重用了non_negative_values通用測試,以檢查amountquantity列的值是否為非負數。

透過這種方式,可以在多個模型中重用相同的測試邏輯,確保資料驗證的一致性,並避免重複編寫測試邏輯。

資料檔案生成與最佳化

在進行資料建模時,適當的檔案生成是不可或缺的組成部分。確保組織中的每個人,包括業務使用者,都能輕鬆理解和存取諸如年度經常性收入(ARR)、淨推薦值(NPS)或每月活躍使用者數(MAU)等指標,對於實作資料驅動的決策至關重要。

使用 dbt 生成檔案

透過利用 dbt 的功能,我們可以記錄這些指標的定義方式以及它們所依賴的特定來源資料。這些檔案成為任何人都可以存取的寶貴資源,促進透明度和啟用自助式資料探索。

範例:記錄 NPS 指標

假設我們有一個名為 nps_metrics.sql 的 dbt 模型,用於計算淨推薦值(NPS)。我們可以使用 SQL 檔案中的註解,並以 Markdown 語法增強,如範例 2-22 所示。

/* nps_metrics.sql
-- 本模型根據客戶反饋計算產品的淨推薦值(NPS)。
依賴關係:
- 本模型依賴於 "feedback" 結構描述中的 "customer_feedback" 表,該表儲存客戶反饋資料。
- 它還依賴於 "users" 結構描述中的 "customer" 表,該表包含客戶資訊。
計算過程:
-- NPS 是透過根據客戶評分將客戶反饋分類別為 Promoters(推薦者)、Passives(中立者)和 Detractors(反對者)來計算的。
-- Promoters:評分為 9 或 10 的客戶。
-- Passives:評分為 7 或 8 的客戶。
-- Detractors:評分為 0 到 6 的客戶。
-- 然後透過從 Promoters 的百分比中減去 Detractors 的百分比來得出 NPS。
*/
-- SQL 查詢:
WITH feedback_summary AS (
  SELECT
    CASE
      WHEN feedback_rating >= 9 THEN 'Promoter'
      WHEN feedback_rating >= 7 THEN 'Passive'
      ELSE 'Detractor'
    END AS feedback_category
  FROM
    feedback.customer_feedback
  JOIN
    users.customer
  ON customer_feedback.customer_id = customer.customer_id
)
SELECT
  (COUNT(*) FILTER (WHERE feedback_category = 'Promoter')
   - COUNT(*) FILTER (WHERE feedback_category = 'Detractor')) AS nps
FROM
  feedback_summary;

內容解密:

  1. 註解說明模型功能:本段註解詳細解釋了 nps_metrics 模型的功能,包括其依賴的資料表和計算 NPS 的過程。
  2. SQL 查詢邏輯:查詢首先使用 CTE(Common Table Expression)feedback_summary 將客戶反饋分類別為 Promoter、Passive 和 Detractor。
  3. 計算 NPS:最終查詢計算 NPS,透過統計 Promoter 和 Detractor 的數量差異來得出。

生成檔案

在記錄模型後,我們可以使用 dbt 命令列介面(CLI)執行以下命令來生成 dbt 專案的檔案(範例 2-23)。

dbt docs generate

執行該命令將為整個 dbt 專案生成 HTML 檔案,包括已記錄的 NPS 指標。生成的檔案可以被託管並提供給組織中的使用者,使他們能夠輕鬆找到並理解 NPS 指標。