这篇不谈大屏样式,只谈供应链项目里建模时真正要先想清楚的几件事。
项目主文:开源供应链数仓
在线演示:oss.zl4629.xyz
01分层不是目录美化
我落地的分层是:
| 层 | 放什么 | 不放什么 |
|---|---|---|
| ODS | API/ZIP 原文 + 拉取审计字段 | 业务过滤、主观删字段 |
| DWD | 统一键后的明细事实 | 跨天滚动排名 |
| DIM | 包、版本、漏洞、生态、日期 | 大量度量 |
| DWS | 固定粒度轻度汇总 | 只服务一张临时报表的怪表 |
| ADS | 榜单、日指标、口径字典 | 回写 DWD |
如果只是把文件丢进 ods/、dwd/ 两个文件夹,没有“能做什么/不能做什么”的约束,分层就只是命名游戏。
02先写清事实粒度
供应链主题域里,我认两张核心事实:
·1. 依赖边 fact_dependency
一句话粒度: 某次解析依赖图中的一条有向边。
from(生态, 包, 版本) -> to(生态, 包, 版本)
relation = DIRECT | INDIRECT注意:
to_version是 deps.dev 解析后的具体版本,不是 range 原文。- 叶子包可以没有边,
edges=0合法。 - 统计“直接依赖数”必须过滤
relation='DIRECT'。
·2. 漏洞影响 fact_vuln_affects
一句话粒度: 一个 OSV 记录对某包的一段版本范围。
osv_id × package × range(introduced/fixed/...)很多记录只有 range,没有完整 versions[]。
所以 ADS 高危榜先做范围级统计,不把“范围命中”直接说成“当前线上版本已中招”。
粒度写不清,后面 SQL 再花也只是在错误明细上做聚合。
03维度表怎么挂
| 维度 | 业务键 | 作用 |
|---|---|---|
dim_ecosystem | pypi / npm | 统一 OSV PyPI 与 deps.dev PYPI 的大小写差异 |
dim_package | 生态 + 包名 | 包主数据;purl;可选 SCD2 |
dim_package_version | 生态 + 包名 + 版本 | 发版时间、是否弃用、许可证 |
dim_vuln | osv_id | 摘要、别名、严重级别 |
dim_date | dt | 分区日、下载日 |
建模时给包、版本、漏洞补 *_sk 代理键,事实表再回挂。
这样 ADS 关联稳定,也方便以后做拉链表(例如仓库信息变更)。
04代表版本:包日汇总的折叠规则
多版本采样时,如果把每个版本的直接依赖相加,包会显得“虚胖”。
包日表使用代表版本:
- 优先
is_default = true - 否则
published_at最新 - 只在该版本上计算 M01 直接依赖
明细边仍保留全版本,汇总和明细职责分开。这是维度建模里很常见、也很容易被忽略的一步。
05总线矩阵
| 事实 | 日期 | 生态 | 包 | 版本 | 漏洞 |
|---|---|---|---|---|---|
| 依赖边 | ✓ | ✓ | ✓ | ✓ | |
| 漏洞 affects | ✓ | ✓ | ✓ | 范围 | ✓ |
| 下载日 | ✓ | ✓ | ✓ | ||
| 发版 | ✓ | ✓ | ✓ | ✓ |
不是所有分析都要上齐维度,但每张事实表必须能说清自己挂哪些维。
06我刻意没做的
- 一上来上全生态 Maven/Go。
- 把 dependents 404 的接口硬写成全网被依赖。
- 在明细层直接做复杂排名宽表。
先把粒度和分层做稳,比急着堆组件名更靠谱。