PostgreSQL 多級依賴血緣系統的設計與落地

一、業務背景:三類指標與四種狀態

指標類型定義規則依賴關系
原子指標單表聚合(SELECT + WHERE + GROUP)
派生指標在原子/派生指標上加 WHERE、改 GROUP依賴 1~N 個父指標
復合指標多個原子/派生指標做加減運算依賴 1~N 個父指標
狀態說明
已保存草稿,可反復修改
已發布對外可見,禁止修改
已落庫數據已固化到 Hive/ClickHouse,禁止修改

約束

  1. 指標不能重復(通過唯一鍵 (name, version) 保證)。

  2. 已發布/已落庫 的指標不允許任何變更;一旦變更,必須級聯同步所有下游。


二、存儲模型:全量 vs 依賴 ID 的抉擇

方案優點缺點結論
全量 SQL 保存查詢快,無級聯問題冗余高、父級改動無法同步?
只存依賴 ID無冗余、天然同步查詢需遞歸或額外緩存?

最終采用“只存依賴 ID + 運行時動態拼裝 SQL”的方案。


三、表結構:指標 & 血緣兩張核心表

1)指標主表?t_indicator

CREATE TABLE t_indicator (id          BIGSERIAL PRIMARY KEY,name        TEXT NOT NULL,version     INT  NOT NULL,type        VARCHAR(16) CHECK (type IN ('ATOMIC','DERIVED','COMPOSITE')),status      VARCHAR(16) CHECK (status IN ('SAVED','PUBLISHED','LOADED')),definition  JSONB,          -- 原子:SQL片段;派生/復合:依賴列表+運算created_at  TIMESTAMPTZ DEFAULT now(),UNIQUE(name, version)
);

2)血緣關系表?t_indicator_dependency

CREATE TABLE t_indicator_dependency (id           BIGSERIAL PRIMARY KEY,indicator_id BIGINT NOT NULL REFERENCES t_indicator(id),parent_id    BIGINT NOT NULL REFERENCES t_indicator(id),depth        INT    NOT NULL,          -- 當前節點到父節點的深度UNIQUE(indicator_id, parent_id)
);-- 常用索引
CREATE INDEX idx_dep_parent ON t_indicator_dependency(parent_id);
CREATE INDEX idx_dep_child  ON t_indicator_dependency(indicator_id);

四、多級依賴查詢:四種實戰方案

方案 A:PostgreSQL 遞歸 CTE(開發階段首選)

-- 查詢節點 100 的所有下游(包括多級)
WITH RECURSIVE down AS (SELECT indicator_id, 1 AS lvlFROM t_indicator_dependencyWHERE parent_id = 100UNION ALLSELECT d.indicator_id, lvl + 1FROM t_indicator_dependency dJOIN down ON d.parent_id = down.indicator_id
)
SELECT * FROM down;

如何快速查詢全線依賴鏈(A ← B ← C ← D ← E)

🔍 場景1:查某個指標的所有上游依賴(如A依賴了誰)

-- 查詢A的所有上游依賴(包括多級)
WITH RECURSIVE upstream AS (SELECT parent_id, 1 AS levelFROM indicator_dependencyWHERE indicator_id = ? -- A的IDUNION ALLSELECT d.parent_id, u.level + 1FROM indicator_dependency dJOIN upstream u ON d.indicator_id = u.parent_id
)
SELECT * FROM upstream;

🔍 場景2:查某個指標的所有下游影響(如E被誰依賴)

-- 查詢E的所有下游影響(包括多級)
WITH RECURSIVE downstream AS (SELECT indicator_id, 1 AS levelFROM indicator_dependencyWHERE parent_id = ? -- E的IDUNION ALLSELECT d.indicator_id, dw.level + 1FROM indicator_dependency dJOIN downstream dw ON d.parent_id = dw.indicator_id
)
SELECT * FROM downstream;

方案 B:封裝成函數,一行調用

CREATE OR REPLACE FUNCTION f_get_rel(node      BIGINT,dir       INT DEFAULT 1,   -- 1 下游;-1 上游;0 雙向max_depth INT DEFAULT 10
)
RETURNS TABLE(direction INT, depth INT, node_id BIGINT) AS $$
BEGINIF dir = 1 OR dir = 0 THENRETURN QUERYWITH RECURSIVE down AS (SELECT 1, 1, indicator_idFROM t_indicator_dependency WHERE parent_id = nodeUNION ALLSELECT 1, d.depth + 1, t.indicator_idFROM t_indicator_dependency tJOIN down d ON t.parent_id = d.node_idWHERE d.depth < max_depth) SELECT * FROM down;END IF;IF dir = -1 OR dir = 0 THENRETURN QUERYWITH RECURSIVE up AS (SELECT -1, 1, parent_idFROM t_indicator_dependency WHERE indicator_id = nodeUNION ALLSELECT -1, u.depth + 1, t.parent_idFROM t_indicator_dependency tJOIN up u ON t.indicator_id = u.node_idWHERE u.depth < max_depth) SELECT * FROM up;END IF;
END;
$$ LANGUAGE plpgsql;

使用示例

SELECT * FROM f_get_rel(100, 1, 5);  -- 下游5層
SELECT * FROM f_get_rel(100, -1, 5); -- 上游5層

方案 C:圖數據庫 Neo4j(可視化 & 超深層級)

MATCH p = (a:Indicator {id: 100})<-[:DEPENDS_ON*]-(b)
RETURN nodes(p) AS chain;

方案 D:閉包表(寫入重、查詢快)

ancestor_iddescendant_iddepth
1001011
1001022

每次變更指標需批量刷新閉包表,適合“讀多寫極少”系統。

? 推薦實踐

  • 中小型系統:優先使用 WITH RECURSIVE,簡潔夠用。

  • 中大型系統:考慮 閉包表 + 緩存,或引入 圖數據庫 做血緣分析。

  • 實時性要求高:用 預構建路徑表緩存方案

? 總結對比表

方案查詢性能寫入成本架構復雜度是否支持實時變更適用場景
RECURSIVE中等?層級淺、變更頻繁
預構建路徑表?讀多寫少、層級深
圖數據庫?復雜血緣、可視化
閉包表?層級深、變更少
緩存+異步刷新極高?(延遲)查詢頻繁、變更低頻

五、狀態級聯校驗:防止“已發布”被改動

1)觸發器:禁止直接 UPDATE 已發布/已落庫指標

CREATE OR REPLACE FUNCTION f_block_if_published()
RETURNS TRIGGER AS $$
BEGINIF EXISTS (SELECT 1 FROM t_indicator WHERE id = NEW.idAND status IN ('PUBLISHED','LOADED')) THENRAISE EXCEPTION 'Cannot modify published/loaded indicator %', NEW.id;END IF;RETURN NEW;
END;
$$ LANGUAGE plpgsql;CREATE TRIGGER trg_block_update
BEFORE UPDATE ON t_indicator
FOR EACH ROW EXECUTE FUNCTION f_block_if_published();

2)級聯校驗:新增派生/復合指標時檢查所有父級狀態

public void validateParents(List<Long> parentIds) {String sql = """SELECT id FROM t_indicatorWHERE id = ANY(?) AND status NOT IN ('PUBLISHED','LOADED')""";List<Long> bad = jdbcTemplate.queryForList(sql, Long.class, parentIds);if (!bad.isEmpty()) {throw new BizException("存在未發布父級: " + bad);}
}

六、線上性能優化三板斧

  1. 緩存:將 f_get_rel() 結果寫入 Redis,Key=rel:{id}:{dir}:{depth},TTL=5 min。

  2. 預編譯:對常用 SQL 模板(原子、派生、復合)提前生成 PreparedStatement。

  3. 異步刷新:指標發布后,通過 MQ 觸發“血緣刷新”任務,預熱緩存。


七、總結

維度實踐要點
存儲只存依賴 ID,避免全量冗余
查詢函數封裝遞歸,必要時圖數據庫
狀態觸發器 + 服務層雙重鎖
性能緩存 + 預編譯 + 異步刷新
可視化Neo4j / ECharts 桑基圖

八、提問:在面對可能存在多級依賴的場景下,針對新建的派生指標,復合指標,指標的定義是保存成全量數據【把父級關系維護一份全量再加上自己的定義整合在一起保存】合適還是只保存父級之間的依賴關系【例如父級id】?

? 推薦方案:只保存依賴關系(父級ID)

1.?避免數據冗余與一致性問題
  • 如果每個派生或復合指標都保存全量定義(包括父級SQL、維度、過濾條件等),一旦父級發生變更(如下線、修訂),子級將無法自動同步,導致邏輯漂移

  • 只保存ID可確保子級始終引用父級的最新定義,通過運行時動態拼裝SQL或邏輯,天然支持依賴鏈同步

2.?滿足“已發布/已落庫不可改”的強約束
  • 全量保存模式下,若父級已發布,子級無法感知父級的“凍結”狀態,可能導致子級基于過期定義構建,違反業務規則。

  • 依賴ID模式下,子級創建時必須校驗父級狀態(如只允許引用已發布父級),系統可在元數據層面強制阻斷非法引用

3.?支持多級依賴的靈活擴展
  • 復合指標可能依賴多個派生指標,派生指標又依賴原子指標,依賴鏈可能非常深

  • 全量保存會導致存儲膨脹(如一個復合指標需冗余存儲整個依賴樹的定義),而ID模式只需記錄有向無環圖(DAG)的邊關系存儲輕量且易于維護

4.?版本控制與回滾更簡單
  • 依賴ID模式下,每個指標定義可獨立版本化(如使用Git-style的版本號),子級通過ID+版本號鎖定父級快照,支持精確回滾

  • 全量保存模式下,任何父級字段的微調都會級聯影響所有子級版本,回滾復雜度呈指數級增長。


?? 需注意的配套設計

  • 運行時解析性能:依賴ID模式需在查詢時動態拼裝SQL/邏輯,可能引入延遲。可通過預編譯緩存(如Flink SQL的物化視圖、Presto的查詢緩存)優化。

  • 循環依賴檢測:需在建模時通過DAG校驗禁止環形引用(如A→B→A)。

  • 父級下線影響:若父級被下線,需級聯校驗所有子級狀態(如提示“依賴指標已下線,請重新編輯”)。


📌 結論

強依賴、強狀態控制、多級衍生的場景下,“只保存依賴關系(父級ID)”是更可持續的設計,配合版本化、狀態校驗和緩存機制,可兼顧靈活性、一致性與性能。

?循環檢測
如果 indicator_dependency沒有循環引用(即不存在 A→B→A 這種環),查詢會正常結束;
如果可能成環,建議加上防環條件,例如

WITH RECURSIVE upstream AS (SELECT parent_id, 1 AS level,ARRAY[indicator_id] AS path   -- 記錄已走過的節點FROM indicator_dependencyWHERE indicator_id = ?               -- A的IDUNION ALLSELECT d.parent_id, u.level + 1,u.path || d.indicator_idFROM indicator_dependency dJOIN upstream u ON d.indicator_id = u.parent_idWHERE NOT d.parent_id = ANY(u.path)  -- 避免環
)
SELECT * FROM upstream;

  1. 返回列完整性
    目前只返回了 parent_idlevel,如果后續還要用到 indicator_id 或其它字段,最好在 CTE 里一并攜帶。

  • 生產環境若存在成環風險,務必加防環條件

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/92353.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/92353.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/92353.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

阿里云百煉平臺創建智能體-上傳文檔

整體思路是&#xff1a; 1創建ram用戶&#xff0c;授權 2上傳文件獲取FileSession 3調用智能體對話&#xff0c;傳入FileSession 接下來每個步驟的細節&#xff1a; 1官方不推薦使用超級管理員用戶獲得accessKeyId和accessKeySecret&#xff0c;所以登錄超級管理員賬號創建…

剪映里面導入多張照片,p圖后如何再導出多張照片?

剪映普通版本暫時沒發現可以批量導出圖片。這里采用其他方式實現。先整體導出視頻。這里前期要注意設置幀率&#xff0c;一張圖片的時長。 參考一下設置&#xff0c;幀率設置為30&#xff0c;圖片導入時長設置為1s&#xff0c;這樣的話&#xff0c;方便后期把視頻切割為單幀。導…

怎么查看Linux I2C總線掛載了那些設備?

1. 根據系統啟動查看設備樹節點文件&#xff08;系統運行后的&#xff09; 比如&#xff1a;要查看I2C2i2c2: i2cfeaa0000 {compatible "rockchip,rk3588-i2c", "rockchip,rk3399-i2c";reg <0x0 0xfeaa0000 0x0 0x1000>;clocks <&cru CLK_…

bat腳本實現獲取非微軟官方服務列表

Get-CimInstance -ClassName Win32_Service |Where-Object { $_.State -eq Running -and $_.StartMode -ne Disabled } | ForEach-Object {$isMicrosoft $false$signerInfo 無可執行路徑if ($_.PathName) {# 提取可執行文件路徑&#xff08;處理帶引號/參數的路徑&#xff09…

小程序難調的組件

背景。做小程序用到了自定義表單。前后端都是分開寫的&#xff0c;沒有使用web-view。所以要做到功能對稱時間選擇器。需要區分datetime, year, day等類型使用uview組件較方便 <template><view class"u-date-picker" v-if"visible"><view c…

從零構建TransformerP2-新聞分類Demo

歡迎來到啾啾的博客&#x1f431;。 記錄學習點滴。分享工作思考和實用技巧&#xff0c;偶爾也分享一些雜談&#x1f4ac;。 有很多很多不足的地方&#xff0c;歡迎評論交流&#xff0c;感謝您的閱讀和評論&#x1f604;。 目錄引言1 一個完整的Transformer模型2 需要準備的“工…

qt qml實現電話簿 通訊錄

qml實現電話簿&#xff0c;基于github上開源代碼修改而來&#xff0c;增加了搜索和展開&#xff0c;效果如下 代碼如下 #include <QGuiApplication> #include <QQmlApplicationEngine>int main(int argc, char *argv[]) {QCoreApplication::setAttribute(Qt::AA_…

順序表——C語言

順序表實現代碼解析與學習筆記一、順序表基礎概念順序表是線性表的一種順序存儲結構&#xff0c;它使用一段連續的內存空間&#xff08;數組&#xff09;存儲數據元素&#xff0c;通過下標直接訪問元素&#xff0c;具有隨機訪問的特性。其核心特點是&#xff1a;元素在內存中連…

【Oracle篇】Oracle Data Pump遠程備份技術:直接從遠端數據庫備份至本地環境

&#x1f4ab;《博主主頁》&#xff1a;    &#x1f50e; CSDN主頁__奈斯DB    &#x1f50e; IF Club社區主頁__奈斯、 &#x1f525;《擅長領域》&#xff1a;擅長阿里云AnalyticDB for MySQL(分布式數據倉庫)、Oracle、MySQL、Linux、prometheus監控&#xff1b;并對…

Linux系統--文件系統

大家好&#xff0c;我們今天繼續來學習Linux系統部分。上一次我們學習了內存級的文件&#xff0c;下面我們來學習磁盤級的文件。那么話不多說&#xff0c;我們開始今天的學習&#xff1a; 目錄 Ext系列?件系統 1. 理解硬件 1-1 磁盤、服務器、機柜、機房 1-2 磁盤物理結構…

KUKA庫卡焊接機器人氬氣節氣設備

在焊接生產過程中&#xff0c;氬氣作為一種重要的保護氣體被廣泛應用于KUKA庫卡焊接機器人的焊接操作中。氬氣的消耗往往是企業生產成本的一個重要組成部分&#xff0c;因此實現庫卡焊接機器人節氣具有重要的經濟和環保意義。WGFACS節氣裝置的出現為解決這一問題提供了有效的方…

遠程連接----ubuntu ,rocky 等Linux系統,WindTerm_2.7.0

新一代開源免費的終端工具-WindTerm github 27.5k? https://github.com/kingToolbox/WindTerm/releases/download/2.7.0/WindTerm_2.7.0_Windows_Portable_x86_64.zip 主機填寫你自己要連接的主機ip 端口默認 22 改成你ssh文件配置的端口 輸入遠程的 用戶名 與密碼 成功連接…

筆試——Day32

文章目錄第一題題目思路代碼第二題題目&#xff1a;思路代碼第三題題目&#xff1a;思路代碼第一題 題目 素數回文 思路 模擬 構建新的數字&#xff0c;判斷該數是否為素數 代碼 第二題 題目&#xff1a; 活動安排 思路 區間問題的貪?&#xff1a;排序&#xff0c;然…

超高車輛如何影響城市立交隧道安全?預警系統如何應對?

超高車輛對立交隧道安全的潛在威脅在城市立交和隧道中&#xff0c;限高設施的設計通常考慮到大部分正常通行的貨車和運輸車輛。然而&#xff0c;一些超高的貨車、集裝箱車或特殊車輛如果未經有效監測而進入限高區域&#xff0c;就可能對道路設施造成極大的安全隱患。尤其在立交…

解決 MinIO 上傳文件時報 S3 API Requests must be made to API port錯誤

在使用 MinIO 進行文件上傳時&#xff0c;我遇到了一個比較坑的問題。錯誤日志如下&#xff1a; io.minio.errors.InvalidResponseException: Non-XML response from server. Response code: 400, Content-Type: text/xml; charsetutf-8, body: <?xml version"1.0&quo…

linux_https,udp,tcp協議(更新中)

目錄 https 加密類型 對稱加密 非對稱加密 加密方案 只用對程加密 只用非對程加密 雙方都是用非對程加密 非對稱對稱加密 非對稱對稱加密證書 流程圖 校驗流程圖 udp udp協議格式 特點 UDP緩沖區 tcp tcp協議格式 32位序號及確認序號 4位首部 6位標志位 1…

web端-登錄頁面驗證碼的實現(springboot+vue前后端分離)超詳細

目錄 一、項目技術棧 二、實現效果圖 ?三、實現路線 四、驗證碼的實現步驟 五、完整代碼 1.前端 2.后端 一、項目技術棧 登錄頁面暫時涉及到的技術棧如下: 前端 Vue2 Element UI Axios&#xff0c;后端 Spring Boot 2 MyBatis MySQL JWT Maven 二、實現效果圖…

瘋狂星期四文案網第33天運營日記

網站運營第33天&#xff0c;點擊觀站&#xff1a; 瘋狂星期四 crazy-thursday.com 全網最全的瘋狂星期四文案網站 運營報告 今日訪問量 今日搜索引擎收錄情況 必應收錄239個頁面&#xff0c;還在持續增加中&#xff0c;已經獲得必應的認可&#xff0c;逐漸收錄所有頁面 百度…

客戶端利用MinIO對服務器數據進行同步

MinIO 是一款高性能、開源的對象存儲服務&#xff0c;專為海量數據存儲設計&#xff0c;兼容 Amazon S3 API&#xff08;即與 AWS S3 協議兼容&#xff09;&#xff0c;可用于構建私有云存儲、企業級數據湖、備份歸檔系統等場景。它以輕量、靈活、高效為核心特點&#xff0c;廣…

WPF 雙擊行為實現詳解:DoubleClickBehavior 源碼分析與實戰指南

WPF 雙擊行為實現詳解:DoubleClickBehavior 源碼分析與實戰指南 文章目錄 WPF 雙擊行為實現詳解:DoubleClickBehavior 源碼分析與實戰指南 引言 一、行為(Behavior)基礎概念 1.1 什么是行為? 1.2 行為的優勢 二、DoubleClickBehavior 源碼分析 2.1 類定義與依賴屬性 2.2 雙…