Community研究與資料分析github.com

ZrobDev/etl-pipelines

用於設計、審查或偵錯批次 ETL/ELT Python 管線:處理重複或遺失資料列、重複計算的回填、浮動水位、延遲到達的資料、重試風暴、破壞載入的結構飄移、爬蟲至倉儲管線、靜默載入零列的工作,以及選擇 cron、n8n、Airflow 或 dlt 等工具。

etl-pipelines 是什麼?

此技能提供設計可靠、冪等批次 ETL/ELT 管線的完整指引,特別適用於 Python 環境,目標儲存庫為 PostgreSQL,並使用 uv、ruff 和 pytest 作為工具鏈。其核心原則在於確保管線對重複執行具有冪等性,也就是說,重新執行任何時間區段的資料擷取都應產生完全相同的結果,藉此避免重複計數或遺失資料。技能內容涵蓋增量載入策略,例如使用水位線(watermark)搭配回顧時間視窗(lookback)來處理延遲資料,也同時討論了全表重建、唯附加事實表等載入模式。此外,它深入探討了實務中的棘手問題,如架構飄移(schema drift)、重試與逾時處理、精確一次語意(exactly-once)的實際可行方案(透過至少一次傳遞加上冪等寫入達成有效一次),以及回填(backfill)的最佳做法。此技能也提供選擇工作調度器(cron、n8n、Airflow、dlt)的決策框架,並包含一個全面的審查清單,確保管線在進入生產環境前已具備可觀測性(observability)與錯誤處理機制。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ZrobDev/data-engineering-skills/tree/HEAD/skills/etl-pipelines

Installed? Explore more 研究與資料分析 skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

ETL 管線

使用 Python (uv, ruff, pytest) 進行批次資料擷取至 PostgreSQL(預設儲存)。本機轉換:polars 2.x 或 DuckDB 2.0。

流程

  1. 定義資料粒度:一列代表什麼,由哪個自然鍵來識別。沒有鍵就沒有冪等載入;請先定義一個(或計算內容雜湊值)。
  2. 選擇載入模式(如下)。選擇增量策略。最後再選擇調度器。
  3. 撰寫執行邏輯,使重新執行任何時間區段都能產生相同的終止狀態。證明它:在測試中執行兩次,並斷言列數與校驗和保持不變。
  4. 在首次生產環境執行前,加入可觀測性檢查(如下)。
  5. 對照章節末的審查清單進行審查。

冪等載入模式

  • 資料列具有穩定的自然鍵且可能會變更:使用 INSERT ... ON CONFLICT (key) DO UPDATE (PostgreSQL) 或 MERGE。加上 WHERE target.hash IS DISTINCT FROM excluded.hash,使未變更的列不會觸發更新。
  • 資料以完整分割區(天、批次 ID)形式到達,並整體重寫:在單一交易中對該分割區執行刪除-插入。若刪除與插入在不同交易中提交,則當當機時會留下空洞。
  • 重建整個資料表:載入到暫存資料表,驗證,然後交換(在單一交易中重新命名)。讀取程式永遠不會看到半載入的資料表。
  • 僅追加的事實表(無鍵):加入確定性的 batch_id / 來源檔案雜湊值,並讓載入程序拒絕或取代已載入的批次 ID。
  • 切勿盲目地執行 INSERT 然後事後去重。重複資料會在被去重之前到達儀表板。
  • 透過 COPY 載入到暫存/臨時資料表,然後執行一次基於集合的更新插入。從 Python 逐列更新插入的速度會慢 10 到 100 倍。

增量載入

  • 根據來源保證為單調遞增且已建立索引的資料欄(updated_at、序列 ID)設定水位線。將其儲存在狀態表中,並在與載入相同的交易中寫入,絕不存放在旁側的檔案中。
  • 讀取 watermark - lookback(例如 1 到 24 小時),而非使用 > watermark。來源的提交並非按時間戳記排序;回顧視窗加上更新插入可吸收延遲到達的資料列。根據測量到的延遲程度來設定回顧視窗大小,而非憑猜測。
  • 水位線資料欄不可靠(更新時未遞增、時區混亂、軟刪除遺漏):執行定期完整調和(按分割區進行鍵值集合差異比較或計數加校驗和)而非信任它。
  • 來源端的硬刪除對水位線是不可見的。明確做出決定:透過鍵值差異來調和,或是接受並記錄此限制。
  • CDC(PostgreSQL 邏輯複寫、Debezium 風格的日誌讀取器):僅在需要處理刪除、次分鐘級延遲,或無法按時間戳記查詢來源時使用。它會引入複寫插槽、保留風險(空閒的插槽會填滿來源磁碟)以及排序/快照處理問題。否則請繼續使用水位線。

回填

  • 每個任務都接受 --start 和 --end(左閉右開區間 [start, end),UTC),並從中推導出所有參數。轉換邏輯中不得使用 now()。
  • 回填就是在舊時間區段上執行一般的任務。如果需要不同的程式碼,代表一般任務不是冪等的;請修正它。
  • 按分割區進行區塊化,獨立執行區塊,記錄每個區塊的狀態,以便在第 40 個區塊失敗時能從第 40 個區塊繼續執行。
  • 透過相同的更新插入/分割區取代操作,回填到同一個目標。檢查無重複計數:在時間區段前後執行 count(*) 和 count(distinct key)。
  • 對來源 API/資料庫進行限流;回填是自找的負載尖峰。

重試、逾時、「精確一次」

  • 在網路環境中不存在精確一次的交付。建立至少一次交付加上冪等寫入;其結果為有效一次。
  • 僅重試冪等步驟,搭配指數退避和抖動、嘗試次數上限,以及每個網路呼叫的強制逾時設定(分別設定連線和讀取逾時)。在遇到 429/5xx/逾時時重試,而非 4xx。
  • 遵循 Retry-After 標頭。沒有全域速率限制的重試會放大服務中斷的影響。
  • 會發送電子郵件、執行收費或傳送到 webhook 的步驟並非冪等:在呼叫前使用儲存的冪等金鑰進行保護。
  • 最終失敗時,以非零狀態碼退出並發出警示;絕不要為了維持排程綠燈而吞掉異常。

結構飄移

  • 落地層是寬鬆的(將原始酬載儲存為 JSON/文字,加上載入中繼資料)。型別化層是嚴格的,並在違反型別或必要欄位規則時大聲失敗。
  • 新增資料欄:可以接受並觸發警示。刪除、重新命名或變更型別的資料欄:使執行失敗並顯示差異;不要將其強制轉換為 NULL。
  • 在邊界處使用明確的結構描述(polars schema、pydantic 或 DuckDB DESCRIBE)進行驗證,僅一次,而非散落在轉換邏輯中。

爬蟲至倉儲

  • 分兩個階段。階段 1 擷取並儲存原始回應,保持不變且不可變(物件路徑以來源、擷取時間、內容雜湊為鍵)。階段 2 將原始資料解析為資料表。
  • 之後修正解析器錯誤時,只需重新解析已儲存的原始資料,無需重新擷取、無封鎖風險,也不會遺失歷史記錄。
  • 在每個已解析的資料列上記錄 URL、擷取時間戳記、HTTP 狀態碼、解析器版本。解析是原始位元組加上解析器版本的純函數。
  • 透過內容檢查來偵測擷取失敗和空白/被封鎖的頁面(驗證碼、回傳 200 但顯示登入牆),而非僅依賴狀態碼。

可觀測性(最低要求)

  • 記錄每個步驟:讀取資料列數、寫入資料列數、拒絕資料列數、持續時間、時間區段起迄、執行 ID。使用結構化(JSON)格式。
  • 斷言不變量:輸出與輸入的資料列數在容許範圍內;對於通常非空白的時間區段若輸出為零列,使執行失敗。靜默的成功零列輸出是最常見的未偵測錯誤。
  • 心跳:在成功時和開始時 ping 一個外部健康檢查端點;在錯過 ping 時發出警示(可偵測 cron 未觸發、主機下線)。失敗時也 ping 失敗端點。
  • 對資料新鮮度(最大已載入時間戳記超過閾值)發出警示,而不僅是任務錯誤。
  • 維護執行記錄資料表(執行 ID、時間區段、狀態、計數);它同時也是您的回填帳本。

選擇調度器

  • Cron(或 systemd 計時器)+ 單一腳本:一到幾個獨立任務,無跨任務依賴關係,團隊能閱讀日誌。預設選項。需要上述的心跳機制和一個鎖(flock)以防止重疊執行。
  • n8n:低資料量、SaaS API 之間的膠水、由 webhook 觸發、非開發人員編輯流程。不適合大量批次、繁重轉換或程式碼審查;將重邏輯保留在其呼叫的 Python 腳本中。
  • Airflow 3.x:大量 DAG、跨任務依賴、一流的回填、重試和 SLA、審計 UI。實際的營運成本(排程器、中繼資料資料庫、工作者);不適合只有少量任務的情況。從 Airflow 2 遷移到 3 是一次包含破壞性變更的遷移;在複製程式碼片段前請先確認版本。若要在 Airflow 上使用 dbt,請使用 Astronomer Cosmos。
  • dlt:適合那些需要結構推斷、增量狀態和合併/取代處理的 API 或資料庫擷取。用它來替代手寫的分頁擷取 + 水位線 + 更新插入程式碼;但仍需對其設定套用上述規則。
  • 容器:將敏感資料設定為 Compose secrets,從 /run/secrets/<name> 讀取,絕不使用環境變數或 env_file;將已發佈的連接埠繫結到 127.0.0.1 或將其保留在內部網路上。

供應商技能

當任務需要 dbt 或 Airflow 的特定細節(模型、測試、DAG 撰寫、運算子、Cosmos 設定)時,請勿即興發揮:應使用透過此倉庫的 de-vendors 技能引入的官方供應商技能,並且僅在需要確認當前版本時,才回退到 references/landscape-2026-11.md 中的版本。

審查清單

  • 已定義鍵值和資料粒度;重新執行相同時間區段會產生相同的狀態(已測試)。
  • 刪除和插入在同一交易中;水位線與載入一起提交。
  • 存在回顧視窗;對延遲資料和刪除有明確的處理方式。
  • 已設定逾時、有限次數的重試(含退避)、對非冪等步驟有保護措施。
  • 原始落地層不可變;解析可分離且可重新執行。
  • 記錄每個步驟的資料列數;已設定零資料列和新鮮度警示;已設定心跳。
  • 結構飄移在型別化層會大聲失敗。
  • 調度器的選擇是根據任務數量和依賴關係,而非習慣。

具體的 SQL 和 Python 骨架:references/patterns.md。來自真實使用的經驗教訓:references/gotchas.md。

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

相關技能