我们做数据治理平台的时候,数据质量检核是绕不开的模块。需求很明确:支持空值检查、唯一性校验、值域合规、跨表一致性、自定义 SQL 规则,支持定时调度和告警,能接入现有的数据治理平台。
面对这个需求,两条路:自建,还是用开源方案。
我们花了三周时间,对 Apache Griffin、Amazon Deequ、Great Expectations 三个开源方案做了深度 POC,同时搭了一个自建原型做对比。最终的选择出乎意料——核心检核引擎自建,辅助能力用开源补位。
这篇文章记录整个选型过程和踩过的坑,希望能帮到面临同样抉择的团队。
Apache 基金会旗下的数据质量方案,2016 年进入孵化器,2018 年毕业。架构上分三块:Measure(检核规则定义)、Job(调度执行)、Service(结果展示)。
技术栈:Spark + Livy + Elasticsearch + MySQL。检核逻辑在 Spark 上执行,通过 Livy 提交 Spark Job,结果写入 ES 和 MySQL。
AWS 开源的数据质量库,基于 Spark 实现。核心是 Analyzer(指标计算)和 VerificationSuite(规则校验),纯库形式,不提供调度、UI、告警等平台能力。
Python 生态最流行的数据质量框架,核心理念是"Expectation"——声明式定义数据应该长什么样。支持 Pandas、Spark、SQL 三种后端,文档和社区活跃度极高。
基于 Spark SQL + 规则引擎的思路,规则配置存 MySQL,Spark 任务定时执行,结果写回 MySQL,前端展示和告警用现有平台能力。
我们用同一份测试数据(5000 万行订单表,32 个字段,Hive 格式,Snappy 压缩),配置了 15 条典型质量规则,在三套方案上跑了一轮完整的 POC。
方案部署组件首次可运行耗时GriffinSpark + Livy + ES + MySQL + ZK2 天DeequSpark(纯库依赖)30 分钟Great ExpectationsPython + Spark/Pandas1 小时自建Spark + MySQL4 小时
Griffin 的部署是最痛苦的。它依赖 Livy 来提交 Spark 任务,但 Livy 本身是一个半死不活的项目(Apache 孵化器,社区活跃度极低),版本兼容性令人抓狂。我们试了 Griffin 0.7 + Livy 0.7,报错;降到 Griffin 0.6 + Livy 0.6,还是报错;最后在 GitHub Issue 里翻到某个老哥的 Hack 方案,手动改了 Griffin 源码里的 Livy 客户端版本才跑通。
结论:Griffin 的部署成本远超预期,不建议在生产环境直接使用。
5000 万行数据,15 条规则:
方案执行时间资源占用Griffin4 分 12 秒8 executors × 4gDeequ2 分 48 秒8 executors × 4gGreat Expectations (Spark)3 分 35 秒8 executors × 4g自建2 分 15 秒8 executors × 4g
Deequ 和自建方案最快,两者的检核逻辑都是直接在 Spark DataFrame 上做聚合操作,没有额外的序列化和中间存储开销。Griffin 最慢,因为它需要经过 Livy 的 REST 接口提交任务,中间多了一层序列化/反序列化,而且它的 Measure 抽象层引入了额外的计算开销。
能力GriffinDeequGreat Expectations自建空值检查✓✓✓✓唯一性✓✓✓✓值域/枚举✓✓✓✓正则匹配✓✗✓✓跨表一致性✓✗✗✓自定义 SQL✓✗✓✓时间窗口✓✓✗✓增量检核✗✓✗✓
Deequ 最大的短板是不支持跨表一致性检查。比如"订单表的客户 ID 必须在客户主数据表中存在"这种引用完整性校验,Deequ 做不了。这在生产环境是刚需。
Great Expectations 的跨表一致性也不支持,它的设计哲学是"一次只检查一个数据集"。
Griffin 的规则定义能力最全,但配置方式非常繁琐——需要通过 JSON 文件定义 Measure,格式复杂,可读性差。
能力GriffinDeequGreat Expectations自建原生 UI✓✗✗可定制调度能力✓✗✗可定制告警通知有限✗✗可定制REST API✓✗✗可定制数据治理平台集成困难灵活灵活无缝
Griffin 有 UI 但不好用。 它的 UI 是 Angular 1.x 写的,2019 年后就没更新过,界面风格和交互体验停留在 5 年前。而且它的前端和后端耦合很紧,想把检核结果嵌入到我们自己的数据治理平台,需要改 Griffin 源码。
Deequ 和 Great Expectations 没有 UI。 它们都是库,不是平台。检核结果需要你自己消费、自己展示、自己告警。如果你已经有数据治理平台,这反而是优势——更灵活。
Livy 依赖是最大痛点。 前面说了部署问题,但更麻烦的是运行时稳定性。Livy 的 Session 管理有严重的内存泄漏问题,长时间运行后 Session 会 OOM,导致 Spark Job 提交失败。我们 POC 期间跑了 3 天,Livy 挂了 2 次。
JSON 规则定义可维护性差。 一条"检查 customer_id 不为空"的规则,Griffin 的 JSON 配置超过 30 行:
{
"name": "accu_check",
"type": "accuracy",
"process.type": "batch",
"data.sources": [{
"name": "source",
"connector": {
"name": "hive",
"version": "1.2",
"config": {
"database": "ods",
"table.name": "orders"
}
}
}],
"evaluate.rule": {
"rules": [{
"dsl.type": "griffin-dsl",
"dq.type": "accuracy",
"rule": "customer_id IS NOT NULL",
"details": {
"source": "source",
"target": "target"
}
}]
}
}30 行 JSON 表达一条"不为空"规则,业务方根本没法自助配置。每次新增规则都要数仓开发来写 JSON,效率极低。
社区活跃度低。 Griffin 的 GitHub 最后一次 Release 是 2021 年的 0.7.0。2022 年至今只有 3 个 commit,基本上处于维护停滞状态。对于一个需要长期维护的基础组件,这个风险不可接受。
Analyzer 和 VerificationSuite 是两个模型,关联逻辑复杂。 Deequ 的 Analyzer 负责计算指标(如完整性、唯一性、均值),VerificationSuite 负责检查规则。但如果你想把 Analyzer 的结果作为 VerificationSuite 的输入(比如"检查完整性是否低于上个月"),需要手动拼接两个 API,代码体验很差。
不支持自定义 SQL 规则。 Deequ 的规则定义基于它的 DSL,无法直接嵌入 SQL。如果你的质量规则是"订单金额 > 0 且订单金额 < 100000",没问题。但如果规则是"执行这段 SQL,结果集为空则通过",做不到。
Scala 优先,Java/Python API 不完整。 Deequ 的核心 API 是 Scala 的,虽然提供了 Python 封装(PyDeequ),但很多高级功能(如 Anomaly Detection、增量检核)在 Python 版中不可用。
Expectation Suite 的 JSON 文件膨胀。 一个包含 50 条规则的 Suite,JSON 文件超过 5000 行。虽然 GE 提供了 CLI 和 Jupyter Notebook 交互式创建 Expectation 的方式,但在生产环境中,规则的管理和维护仍然是个问题——版本控制、规则复用、批量修改,都不方便。
Python 生态,与 Java/Spark 主栈的集成有摩擦。 我们的数据平台是 Java 技术栈,GE 是纯 Python 项目。虽然可以通过 Airflow 调度 GE 的 Python 脚本,但在日志、监控、告警等环节的集成成本比纯 Java 方案高。
Data Docs 好看但不够实用。 GE 的 Data Docs 是它的特色功能——自动生成漂亮的数据质量报告。但静态 HTML 报告在企业场景下不够用,你需要的是一个 API,让前端按需查询检核结果,而不是打开一个 HTML 文件。
综合 POC 结果和团队情况,我们最终选择了自建核心检核引擎,部分能力用开源方案补位。
架构很简单:
定时调度(Azkaban/DolphinScheduler)
→ 拉取规则配置(MySQL)
→ 生成 Spark SQL
→ 提交 Spark Job
→ 结果写回 MySQL
→ 前端展示 + 告警规则配置表设计:
CREATE TABLE dq_rule (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
rule_name VARCHAR(200) NOT NULL COMMENT '规则名称',
rule_type VARCHAR(50) NOT NULL COMMENT '规则类型:NOT_NULL/UNIQUE/RANGE/REGEX/CROSS_TABLE/CUSTOM_SQL',
target_db VARCHAR(100) COMMENT '目标库',
target_table VARCHAR(100) NOT NULL COMMENT '目标表',
target_col VARCHAR(100) COMMENT '目标字段',
rule_config TEXT NOT NULL COMMENT '规则配置JSON',
expect_result VARCHAR(50) COMMENT '期望结果:PASS/FAIL',
schedule_cron VARCHAR(50) COMMENT '调度表达式',
enabled TINYINT DEFAULT 1,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);规则配置 JSON 示例:
{
"type": "NOT_NULL",
"threshold": 0.99,
"comment": "customer_id 完整率不低于 99%"
}{
"type": "CROSS_TABLE",
"source_table": "ods.orders",
"source_col": "customer_id",
"target_table": "dim.customer",
"target_col": "id",
"join_type": "LEFT_ANTI",
"comment": "订单表的客户ID必须在客户主数据表存在"
}{
"type": "CUSTOM_SQL",
"sql": "SELECT COUNT(*) FROM ods.orders WHERE order_amount <= 0 OR order_amount > 100000",
"expect": "ZERO",
"comment": "订单金额不得为负数或超过10万"
}Spark 执行逻辑(核心代码):
def executeRule(rule: DqRule, spark: SparkSession): DqResult = {
rule.ruleType match {
case "NOT_NULL" =>
val config = parseConfig(rule.ruleConfig)
val totalCount = spark.sql(s"SELECT COUNT(*) FROM ${rule.targetTable}").collect()(0).getLong(0)
val nullCount = spark.sql(
s"SELECT COUNT(*) FROM ${rule.targetTable} WHERE ${rule.targetCol} IS NULL"
).collect()(0).getLong(0)
val ratio = 1.0 - nullCount.toDouble / totalCount
DqResult(rule.id, rule.ruleName, ratio >= config.threshold, ratio, s"完整率: ${ratio * 100}%")
case "CROSS_TABLE" =>
val config = parseConfig(rule.ruleConfig)
val violateCount = spark.sql(
s"""SELECT COUNT(*) FROM ${config.sourceTable} s
|LEFT ANTI JOIN ${config.targetTable} t
|ON s.${config.sourceCol} = t.${config.targetCol}""".stripMargin
).collect()(0).getLong(0)
DqResult(rule.id, rule.ruleName, violateCount == 0, violateCount,
s"引用完整性违规: ${violateCount} 条")
case "CUSTOM_SQL" =>
val config = parseConfig(rule.ruleConfig)
val result = spark.sql(config.sql).collect()(0).getLong(0)
val pass = config.expect == "ZERO" && result == 0
DqResult(rule.id, rule.ruleName, pass, result, s"自定义SQL结果: ${result}")
}
}自建规则引擎覆盖了 80% 的日常检核场景,但有一个能力我们没做——数据画像和异常检测。
Deequ 的 Analyzer 和 Anomaly Detection 在这块做得很好。我们把它作为辅助模块集成进来:每周跑一次完整的数据画像(完整性、唯一性、均值、标准差、直方图),用 Deequ 的 AnomalyDetectionStrategy 检测指标异常。
import com.amazon.deequ.analyzers._
import com.amazon.deequ.anomalydetection.{AnomalyDetectionStrategy, RelativeRateOfChangeStrategy}
val analyzers = Seq(
Completeness("customer_id"),
Uniqueness("order_id"),
Mean("order_amount"),
StandardDeviation("order_amount")
)
val analysisResult = AnalysisRunner
.onData(df)
.addAnalyzers(analyzers)
.useRepository(metricsRepository) // 结合历史指标做异常检测
.run()项目自建开源方案首次开发3 人月1 人月持续维护0.5 人月/年1 人月/年(修兼容问题、跟进社区)定制化成本低高平台集成原生需二次开发长期风险依赖团队依赖社区活跃度
自建的前期投入更高,但长期维护成本更低,定制化效率远超开源方案。
如果你的团队面临同样的抉择,我建议按以下决策树判断:
是否需要 UI 和调度能力?
├─ 需要 → 团队有 Java 技术栈? → 自建
├─ 需要 → 团队是 Python 栈? → Great Expectations + Airflow
└─ 不需要 → 继续
是否需要跨表一致性检查?
├─ 需要 → 自建 或 Griffin(如果接受部署复杂度)
└─ 不需要 → 继续
核心检核逻辑是否能用 SQL 表达?
├─ 能 → 自建(成本最低,效果最好)
└─ 不能 → Deequ(复杂统计分析场景)
是否需要长期维护和社区支持?
├─ 需要 → 自建 或 Great Expectations
└─ 不需要 → Griffin(如果团队能接受停更风险)数据质量检核引擎在技术上没有银弹——开源方案看起来"拿来就用",但真正接入生产环境后,你会发现大量的时间花在修兼容问题、补缺失功能、改 UI 集成上。这些时间加起来,往往不比自建少。
自建不意味着从零开始。 用 Deequ 做指标分析,用 Great Expectations 做数据文档,用 Spark SQL 做规则检核——把开源方案当作"库"而不是"平台",各取所长,组装成适合自己的方案。
技术选型的最高境界,不是选最好的方案,是选最适合你团队能力和业务场景的方案。
本文基于作者团队在数据治理平台建设中的真实选型经验。性能数据来自 5000 万行测试数据集,实际生产环境数据量级不同,结论可能不同,请以自身 POC 结果为准。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。