4. 数据质量规则引擎:规则定义语法、规则分类与执行引擎设计
聊到数据质量,很多人第一反应是「写脚本跑检查」。没错,脚本能干活,但项目一多你就知道痛了——每个脚本都是孤岛,规则散落在代码里,改个阈值得翻半天。我经历过一个数据仓库项目,光空值检查就写了三十多个 Python 脚本,后来想统一管理,差点没把自己逼疯。
所以,我们需要一个规则引擎。说白了,就是把「怎么检查数据」这件事,从代码里抽出来,变成可配置、可复用、可扩展的规则。这一章,我就带你把这个引擎的骨架搭起来。
4.1 规则定义语法:让业务人员也能写规则
规则引擎的第一步,是定义规则「长什么样」。我个人习惯用 JSON 或 YAML 来描述规则,结构清晰,解析也方便。下面是我常用的规则定义模板:
{
"ruleId": "DQ-001",
"ruleName": "用户邮箱格式校验",
"ruleType": "FORMAT_CHECK",
"target": {
"datasource": "hive",
"table": "ods_user_info",
"column": "email"
},
"condition": {
"operator": "regex_match",
"value": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
},
"severity": "ERROR",
"action": "BLOCK"
}
这里有几个关键字段:
- ruleId:规则唯一标识,方便追踪和排错。
- ruleType:规则分类,后面会细讲。
- target:检查对象,数据源、表、字段。
- condition:检查逻辑,包括操作符和期望值。
- severity:严重级别,WARN / ERROR / FATAL。
- action:触发后的动作,比如阻断、告警、跳过。
4.2 规则分类:四种核心检查类型
规则分类是引擎的骨架。我一般把规则分成四大类,每一类对应不同的检查场景。你想想看,数据质量问题翻来覆去就那几样——空值、格式、范围、业务逻辑。
4.2.1 空值检查
最基础,也最容易忽略。空值检查不只是判断 NULL,还要考虑空字符串、占位符(比如 'N/A'、'-')。
{
"ruleType": "NULL_CHECK",
"condition": {
"operator": "is_null_or_empty",
"value": null
},
"threshold": 0.05 // 允许 5% 的空值率
}
我在项目中遇到过,某个上游系统用 'NULL' 字符串表示空值,结果空值检查一直没触发。后来加了自定义空值列表才搞定。
4.2.2 格式校验
邮箱、手机号、身份证、日期……格式校验靠正则表达式。但要注意,正则不是万能的。
{
"ruleType": "FORMAT_CHECK",
"condition": {
"operator": "regex_match",
"value": "^1[3-9]\\d{9}$"
}
}
4.2.3 范围校验
数值、日期、枚举值,都在范围内。范围校验支持闭区间、开区间、枚举列表。
{
"ruleType": "RANGE_CHECK",
"condition": {
"operator": "between",
"min": 0,
"max": 150,
"includeMin": true,
"includeMax": false
}
}
嗯,这里要注意:年龄范围校验,0 岁和 150 岁,边界值很容易踩坑。我建议 always 明确 includeMin/includeMax。
4.2.4 业务逻辑校验
这是最复杂的一类。业务逻辑校验往往涉及跨字段、跨表甚至跨系统的关联检查。
{
"ruleType": "BIZ_LOGIC_CHECK",
"condition": {
"operator": "cross_field_compare",
"fields": ["order_amount", "payment_amount"],
"relation": "order_amount >= payment_amount"
}
}
举个例子:订单金额必须大于等于支付金额。这个逻辑看起来简单,但实际数据里经常出现订单金额为 0 但支付金额为正的异常。为什么会这样?因为上游系统在退款场景下没处理好。
4.3 规则执行引擎设计:从配置到执行
规则定义好了,谁来跑?怎么跑?这就是执行引擎的事。我设计的引擎分三层:调度层、解析层、执行层。
4.3.1 引擎架构总览
先看一张图,把整体结构理清楚:
4.3.2 调度层:什么时候跑
调度层负责决定「什么时候执行」。我一般支持三种触发方式:
- 定时触发:每天凌晨跑一次,适合离线数仓。
- 事件触发:数据入库后自动触发,适合实时管道。
- 手动触发:排查问题时用,或者上线前全量跑一遍。
调度层还要处理依赖关系。比如,表 A 的规则跑完,才能跑表 B 的规则。我曾经因为没处理好依赖,导致下游表用上游的脏数据跑了一整天,修复成本极高。
4.3.3 解析层:规则怎么理解
解析层是引擎的大脑。它把 JSON 规则翻译成可执行的检查计划。核心步骤:
- 语法校验:检查 JSON 结构是否完整,字段是否合法。
- 参数绑定:把规则里的占位符替换成实际值。
- 规则分类路由:根据 ruleType 把规则分发给对应的校验器。
- 执行计划生成:合并相同字段的规则,减少扫描次数。
4.3.4 执行层:规则怎么跑
执行层是干活的人。每个校验器只负责一种检查类型,职责单一。执行流程:
- 从解析层拿到执行计划。
- 连接数据源,读取目标数据。
- 逐条或批量执行检查。
- 收集结果,判断是否通过。
- 如果失败,根据 severity 执行对应动作。
执行层还要处理异常。比如,数据库连接超时、字段不存在、正则语法错误。我习惯加一个重试机制,最多重试 3 次,间隔 5 秒。如果还失败,就标记为「执行异常」,人工介入。
4.4 规则执行结果与反馈
规则跑完,结果怎么呈现?我一般用一张表来记录每次执行的详细信息:
| 字段 | 类型 | 说明 |
|---|---|---|
| execution_id | string | 执行批次 ID |
| rule_id | string | 规则 ID |
| check_time | timestamp | 检查时间 |
| total_rows | int | 检查总行数 |
| failed_rows | int | 失败行数 |
| status | string | PASS / FAIL / ERROR |
| detail | json | 失败样本数据 |
有了这张表,你可以做很多事:趋势分析、规则命中率统计、数据质量评分。我个人习惯每天跑完规则后,自动生成一份质量报告,发给数据负责人。
4.5 小结
这一章我们聊了规则引擎的三个核心部分:规则定义语法、规则分类、执行引擎设计。规则定义语法让规则变得可配置、可管理;规则分类帮你快速定位问题类型;执行引擎则把规则从配置变成行动。
你想想看,有了这套引擎,数据质量检查不再是散落的脚本,而是一个可扩展、可维护的系统。下次业务方说「帮我加个检查」,你只需要写一条 JSON 规则,而不是改代码、上线、测试。这才是数据治理该有的样子。