数据仓库是一门极其成熟的数据工程。简单表述一下23年我们用几个月时间重新梳理数据仓库的方式,一是做记录留念,二是以备后效。
## 重构之前的问题
- 命名无规范。
- 中间层级少,导致 dwt 层重复计算多。
- 数据异常无监控
- 代码无版本管理
- 调度和代码耦合,导致多次调度需要重复的代码。
## 系统简介
我们采用了这些组件:hive, clickhouse, dolphinschedular, guandata BI, 自建的web报表。
## 重构的流程
### 分层规划
重点是在上一版的常规的 ods-dwd-dwt 的分层上做了调整:
- 增加 dwe 错误报警层。每一张 dwd 表都有一张对应的 dwe 表,当数据出现逻辑错误时,错误数据可以过滤到 dwe 层。
- 且 每一个 ods 表都有一张对应的 dwd 表,从 ods 到 dwd 的时候先过滤掉无用数据。所有数据应用的计算不允许查询 ods 层,最上游只能使用到 dwd 层,避免源头数据异常造成影响。
- app 层改名为 dws 层(轻度汇总层),更加符合业务含义。
- dwt 层和 dws 都支持对外服务。之前仅 app 层支持对外服务。
- dwe 层也作为 dws dim 等层的错误报警层,通过表名判断识别。
- 借助 BI 系统的数据清理功能,在 BI 上增加一层数据校正的逻辑,使得可以在不改动业务系统数据的前提下,校正报表上应异常业务产生的错误数据。
改进后的优劣:
(1)BI 数据校正层:
- 好处:业务人员当场可以处理数据错误,报表可以第一时间正确可用。
- 坏处:源头异常的修复结果至少需要1天后才到 ods,造成临时的数据不一致。
(2)数据仓库数据纠错层
- 好处:取消了之前 java 项目的错误数据监控方案,直接在同一个数据系统里用 sql 开发错误监控脚本,成本和复杂读就低不少。
- 坏处:略微增加了数据仓库的计算量。但最终我们也只对极个别的核心表做了异常纠错,可以认为几乎无新增数据开销。
(3)跨层调度的规则明确:dwd 和 dwt 允许同层调用,其他数据只能单向流动。
- 好处:新增字段等新需求开发时,字段上推到向上一层的结果十分容易判断。
- 坏处:时间久了,出现一些无用的混杂的 dwd 表。
于是,得到了这样的分层结构:
- 后来,大部分 BI 数据层数据改成直连查询 clickhouse,少了几个小时的数据同步时间。
![[花了几个月重构了数据仓库20240930144832.png]]
### 分域规划
把数据域和分析主题域强行分开,让数据研发在开发时提前明确该表的首要作用是记录数据(数据域)还是面向应用(分析主题域)。不同作用方向的字段开发,侧重点不同。记录数据的,尽量保持原状,避免造出大量干扰人的新字段;面向应用的,可以按照业务需要,新建字段,方便下游系统使用。
#### 数据域(业务系统域)
- 在 ods 层 和 dwd 层的第一子层,用于标识数据来源的业务系统名。
#### 分析主题域
- 在 dwd 层的第一子层(不含)以下的其他层,用于标识消费该数据的业务域。
- 基本上,每个业务部门拥有一到几个分析主题。当A部门的分析主题也是B部门的分析主题时,按当前数据表的上游主要数据来源表判断分析主题域,如果是高频的分析主题,可单独设立一个主题域,这个主题域的数据由相关部门共用。
#### 业务过程
- 在每个分析主题域下,涉及多个业务过程。先规范好业务过程名,避免命名混乱,含义不清。
### 命名规范
#### 表命名规范
表名遵循以下格式,[] 表示可选。
`数据库名.分层名_业务系统域/分析主题域_[二级主题]_表名_存储信息_更新周期`
- 数据库名
- 其实就是分层名,在 Hive 中每一层都单独分一个数据库。
- 分层名。即属于数据仓库哪一层。一个单词。
- 业务系统域/分析主题域。
- ods 和大部分的 dwd 需要标明来源业务系统名称。
- dwt dws 和部分的 dwd 需要标明所属分析主题。
- dim 如果属于公共维度,则可以不写主题域;如果存在明确分析主题域的,可以写分析主题域。
- 表名。多个单词,准确描述该表的主要信息。
- 暂时不对表名的具体命名方式做太多的限制。
- 建议表名尽量包含:最细粒度的主体名、业务过程名。
- dwe 表的表名的 `业务域/主题域_[二级主题]_表名`这一段应该和其服务的表保持一致。
- 注意,某些 dwd 表到 dwt 层时`业务域/主题域_[二级主题]_表名`这一段可能未做修改,此时会导致 dwe 表表名重复。为避免这种冲突,dwe 的表名的分层名应该追加它服务的表的分层名。例如
- `dwd.dwd_domaina_<table>_d + dwe.dwe_dwd_domaina_<table>_d`
- 存储信息。全量或增量、是否快照等。
- 不分区:无需填写存储信息
- 或者按医院分区。
- 全量分区:full。
- 增量分区:inc
- 快照:ss
- 拉链表:zip
- ...(其他按需补充)
- 更新周期(有分区的表,基本上更新周期和分区的周期保持一致;无分区的表,更新周期视情况设置即可)
- 手动更新:不加
- 按小时 h
- 按天 d
- 按周 w
- 按月份 m
- 按年 y
### 开发规范(建议)
因为我是数据产品,所以当时只给了一些建议。
#### DWD 开发要求
- 每一张 dwd 表需要明确主键,即明确数据粒度。建议在表 comment 信息中简要描述。
- 理论上 dwd 的表都应该是最细粒度的数据。
- 每一张 dwd 表的每一个字段,数据仓库开发应当完全理解其含义及其取值可能性。建议在字段 comment 信息中简要描述。过于复杂的,建议在飞书中建立文档用于描述。
- 无法理解的字段,不应该进入 dwd 层。无需担心遗漏字段,未来可以将需要的字段重新加到 dwd 层。
- 每一张 dwd 表需要相应的 dwe 表,用于记录错误信息。除非这张表特别干净整洁。
#### DWT 和 DWS 开发要求
- 每一张 dwt 和 dws 表需要明确主键,即明确数据粒度。建议在表 comment 信息中简要描述。
- 每一张 dwt 和 dws 表的每一个字段,数据仓库开发应当完全理解其含义及其取值可能性。建议在字段 comment 信息中简要描述。过于复杂的,建议在飞书中建立文档用于描述。
- 需要对外公开使用的 dwt 和 dws 表,应该配备相应的字段说明文档,便于用户理解字段。
- 如有必要,可以配置相应的 dwe 表,用于记录错误信息。除非这张表特别干净整洁。
#### 数仓表变更约等于功能发版
- 每一层的每一张表的信息记录、字段说明、历史重大变更等,特别是业务含义、业务逻辑等的变化,应当在文档中可追踪。这样使用者和未来新入职的数据仓库开发,可以通过看表结构和表文档理解 90% 的含义。
- 已经上线的 dwd dwt dws 表,所有变更谨慎评估对线上报表的影响后,再安排上线计划。重大逻辑变更需要在 xxx(我们的数据仓库文档入口页)对应的下层文档中记录。
- 宁愿慢,不能错。
### 调度规范
我们采用了 dolphinschedular 作为调度系统。之前的用法随意,积累了不少问题:
- 跨数据库处理数据,数据溯源难。同时在 Clickhouse 和 Hive 中写数据处理逻辑,导致需要查找多个上游平台的数据处理逻辑。有时候取个数都要分两步取,先在 hive 处理一下,再把数据灌入 dwt 处理一下。为此,在 Hive 中增加 dwt 层,再完整复制 dwt 层数据到 clickhouse。让 clickhouse 作为查询加速的组件用。
- 单表多脚本。为了满足不同分析主题需求,或者为了满足单日多次跑数的需求,在 dolphinschedular 中创建了多份调度工作流,导致某张表的写入逻辑存在 2 份脚本,造成运维隐患。现在将数据处理工作流和调度工作流分开,改成每个表有自己的数据处理和写入工作流,另外新增调度工作流专门负责作业编排和调度工作。增加数据更新批次,执行额外新增调度工作流即可。
- 工作流定位难。无法直接用表名直接查找到对应的工作流,必须记住每一个表处于某个工作流中,然后去工作流中寻找。前述的“将数据处理工作流和调度工作流分开”,也可以解决这个问题。每个表的自己的数据处理和写入工作流,直接用表名命名。在项目目录下直接搜索即可定位。
- 单表单工作流,还能做到代码版本管理。
具体规范如下:
- 每张表从 ods -> dim 或者 ods -> dwd 时都应该新建一份“数据处理和写入工作流”,工作流的名称和表名保持一致。
- dwe 的任务可以放在对应的表的工作流里,无需单独新建一个工作流。寻找表的 sql 时,直接搜索表名即可打开看到该表所有的处理逻辑。
- Hive 的 dwt 层可以同步到 BI 中使用,建议在 ds 中该表的工作流的末尾增加 shell 节点触发数据抽取。
- 每一层的数据生成,都可以单独采用一个调度工作流。
- 数据源 > ods 层
- ods 层 > dim 层
- ods 层 > dwd/dwe 层
- dwd 层 > dwt 层
- dwt 层 > dws 层
- 特殊的调度任务,可以单独建立一个调度工作流。此时,引用“数据处理和写入工作流”即可,无需重复新建“数据处理和写入工作流”。
规范后效果
- 关闭了十几个项目,合并到 2 个项目里。其中一个项目时低频使用的老脚本,不打算迁移了,只等待逐个停用。最终可以保持所有的工作流在一个项目里(目前还未实现)。我们的业务不复杂,所以一个项目够了。
- 寻找表更方便了。之前改一个逻辑,寻找代码都要找好久。