一、业务背景:三类指标与四种状态

指标类型定义规则依赖关系
原子指标单表聚合(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 双…