一、一个真实的问题现场

上个月,某快消品企业的市场部找到数据团队兴师问罪。

事情是这样的:他们的数据中台项目上线三个月了,ODS → DWD → DWS → ADS 整条链路跑得稳稳当当,每天凌晨准时产出报表。市场部想基于用户行为数据做一波精准营销——取近30天有加购但未下单的用户,推送优惠券。

结果呢?推了3万条短信出去,退货率没降,客诉率翻了3倍。追查下来发现:用户标签表里 phone 字段的乱码率接近12%,空值率7%,大量短信发到了无效号码上,还有一部分发给了半年前已经注销的用户。

市场总监的原话是:“你们不是说数据中台跑通了吗?”

这个问题戳中了很多数据团队的痛点——ETL 跑通 ≠ 数据能用

根据 DAMA(国际数据管理协会)《数据管理知识体系指南》(DMBOK)的定义,数据质量管理的核心维度包括完整性(Completeness)、一致性(Consistency)、准确性(Accuracy)、及时性(Timeliness)、唯一性(Uniqueness)和有效性(Validity)。一个"跑通"的 ETL 管道只能保证数据的流转,无法保证数据在这些维度上满足业务消费的要求。

二、强校验 vs 旁路监测:两种数据质量治理范式

2.1 强校验模式(Hard Validation)

强校验的本质是在数据写入时进行规则判定,不满足质量规则的数据直接拒绝入库。这是传统 ETL 中最常见的做法:

-- 强校验示例:不符合规则的数据直接丢弃或写入错误表
INSERT INTO dwd_user_profile
SELECT * FROM ods_user_behavior
WHERE phone IS NOT NULL
  AND phone REGEXP '^1[3-9][0-9]{9}$'
  AND user_status != 'CANCELLED';
-- 不满足条件的数据直接丢失,没有任何记录

强校验的问题——它假设你预先知道所有数据质量问题。在实际生产环境中,数据质量问题是持续涌现的:

  • 上游业务系统字段扩展(新增了 international_phone 格式)
  • 埋点 SDK 版本升级导致事件字段变更
  • 第三方数据源接口返回结构变化
  • 人工录入环节的操作规范变化

这些变化无法提前穷举,强校验会把大量"未知质量状态"的数据挡在门外,而挡在门外的数据不等于不存在——你只是看不见它们了。

2.2 旁路监测模式(Bypass Monitoring)

旁路监测的核心思想来自 DAMA DMBOK 中的数据质量评估(Data Quality Assessment)方法论:数据质量不应该成为数据流通的阻塞点,而应该作为可观测性指标持续追踪

旁路监测的架构位置示意:

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│   Source     │────▶│  ETL Engine  │────▶│   Target     │
│   System     │     │  (正常流转)   │     │   Table      │
└──────────────┘     └──────┬───────┘     └──────────────┘
                            │
                      ┌─────▼──────┐     ┌──────────────┐
                      │  Bypass    │────▶│ 质量报告/告警  │
                      │  Monitor   │     │  (异步输出)    │
                      └────────────┘     └──────────────┘

两者的本质差异:

维度强校验模式旁路监测模式
阻断策略同步阻断,不满足即拒绝异步监测,数据正常流转
数据完整性可能丢失数据全量数据保留
灵活性规则变更需重启任务规则热更新,不影响数据管道
问题感知被动(阻塞时才暴露)主动(持续输出质量指标)
适用场景已知的硬约束(主键非空等)探索性质量监控、渐变式问题发现
架构耦合度与 ETL 流程紧耦合与 ETL 流程松耦合

在方法论层面,龙石数据在中台实践中参考 DAMA 数据质量管理框架,将旁路监测定位为**数据质量可观测性(Data Quality Observability)**的基础设施,与 ETL 管道解耦,实现"流算分离、质算分离"。

三、旁路监测配置实操:6步搭建教程

以下是一个基于实际项目的旁路监测配置完整流程。技术栈:Spark Streaming + Kafka + 规则引擎 + Prometheus/Grafana。

Step 1:定义数据质量规则模型

首先设计规则元数据表结构:

CREATE TABLE dq_monitor_rule (
    rule_id          VARCHAR(64)  PRIMARY KEY COMMENT '规则唯一标识',
    rule_name        VARCHAR(256) NOT NULL COMMENT '规则名称',
    target_table     VARCHAR(128) NOT NULL COMMENT '目标表名(监控对象)',
    target_field     VARCHAR(128) COMMENT '目标字段名(字段级监控时必填)',
    rule_type        VARCHAR(32)  NOT NULL COMMENT '规则类型:NULL_CHECK/REGEX/RANGE/UNIQUE/CONSISTENCY/CUSTOM',
    rule_expression  TEXT         COMMENT '规则表达式(EL表达式/正则/SQL片段)',
    severity         VARCHAR(16)  NOT NULL DEFAULT 'WARN' COMMENT '严重级别:INFO/WARN/ERROR/CRITICAL',
    threshold_pct    DECIMAL(5,2) COMMENT '异常阈值百分比,如5.00表示超过5%触发告警',
    enabled          TINYINT(1)   NOT NULL DEFAULT 1 COMMENT '是否启用',
    created_at       DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at       DATETIME     ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_target (target_table, target_field),
    INDEX idx_enabled (enabled)
) COMMENT '数据质量监测规则配置表';

Step 2:设计EL表达式规则引擎

规则表达式采用 Spring EL(Expression Language) 扩展语法,支持上下文变量注入:

/**
 * 规则表达式上下文变量定义
 * #{fieldValue}    - 当前字段值
 * #{rowData}       - 当前行数据 Map<String, Object>
 * #{metaData}      - 元数据上下文(表名、分区、时间戳等)
 * #{refData}       - 引用数据(字典表、主数据查询结果)
 */
public class DqRuleContext {
    private Object fieldValue;
    private Map<String, Object> rowData;
    private Map<String, String> metaData;
    private Map<String, Object> refData;
    // getters & setters ...
}

规则表达式示例表:

-- 示例1:空值检查 - phone字段非空率监控
INSERT INTO dq_monitor_rule VALUES (
    'RULE_001', '用户手机号非空检查', 'dwd_user_profile', 'phone',
    'NULL_CHECK', '#{fieldValue} != null', 'ERROR', 5.00, 1, NOW(), NULL
);

-- 示例2:正则校验 - 手机号格式校验
INSERT INTO dq_monitor_rule VALUES (
    'RULE_002', '手机号格式正则校验', 'dwd_user_profile', 'phone',
    'REGEX', '#{fieldValue} matches ''^1[3-9]\\d{9}$''', 'WARN', 10.00, 1, NOW(), NULL
);

-- 示例3:范围检查 - 订单金额合理性
INSERT INTO dq_monitor_rule VALUES (
    'RULE_003', '订单金额范围检查', 'dwd_order_detail', 'order_amount',
    'RANGE', '#{fieldValue} >= 0 and #{fieldValue} <= 999999', 'ERROR', 3.00, 1, NOW(), NULL
);

-- 示例4:一致性检查 - 用户状态与注册时间逻辑一致性
INSERT INTO dq_monitor_rule VALUES (
    'RULE_004', '用户状态一致性检查', 'dwd_user_profile', NULL,
    'CONSISTENCY', 
    '(#{rowData[''user_status'']} != ''CANCELLED'') or (#{rowData[''cancel_time'']} != null)',
    'WARN', 2.00, 1, NOW(), NULL
);

-- 示例5:自定义检查 - 复杂业务逻辑(参考维表)
INSERT INTO dq_monitor_rule VALUES (
    'RULE_005', '产品类目有效性校验', 'dwd_product_info', 'category_code',
    'CUSTOM', 
    '#{refData[''valid_categories''].contains(fieldValue)}',
    'ERROR', 1.00, 1, NOW(), NULL
);

Step 3:实现旁路采集器(Bypass Collector)

核心思路:在数据写入目标表的同时,异步采样发送到 Kafka 监测 Topic。

// 旁路采样采集器 —— 零侵入设计
@Component
public class DqBypassCollector {

    @Value("${dq.sample.rate:0.1}")  // 默认10%采样率
    private double sampleRate;

    @Autowired
    private KafkaTemplate<String, DqSampleEvent> kafkaTemplate;

    private final ThreadLocalRandom random = ThreadLocalRandom.current();

    /**
     * 在数据写入目标表后调用,异步采样发送到监测通道
     * @param tableName  目标表名
     * @param partition  数据分区
     * @param rowData    行数据
     */
    @Async("dqExecutor")
    public void collect(String tableName, String partition, Map<String, Object> rowData) {
        // 采样率控制:避免全量监测影响性能
        if (random.nextDouble() > sampleRate) return;

        DqSampleEvent event = DqSampleEvent.builder()
            .tableName(tableName)
            .partition(partition)
            .rowData(rowData)
            .collectTime(System.currentTimeMillis())
            .sampleRate(sampleRate)
            .build();

        kafkaTemplate.send("dq-bypass-samples", event);
    }
}

数据管道中嵌入采集点(以 Flink 为例):

// Flink DataStream 中嵌入旁路采集
dataStream
    .map(record -> {
        // 原有ETL转换逻辑
        DwdUserProfile profile = transform(record);

        // 旁路采样发送(异步,不影响主流程延迟)
        dqCollector.collect("dwd_user_profile", partition, toMap(profile));

        return profile;
    })
    .addSink(targetSink);

Step 4:构建规则评估引擎

@Component
public class DqRuleEvaluator {

    @Autowired
    private DqRuleRepository ruleRepository;

    @Autowired
    private ExpressionParser parser;  // Spring EL Parser

    /**
     * 对采样数据进行规则评估,返回触发的质量问题列表
     */
    public List<DqViolation> evaluate(DqSampleEvent sample) {
        List<DqMonitorRule> rules = ruleRepository
            .findEnabledByTable(sample.getTableName());

        List<DqViolation> violations = new ArrayList<>();

        for (DqMonitorRule rule : rules) {
            DqRuleContext ctx = buildContext(rule, sample);
            EvaluationContext evalCtx = new StandardEvaluationContext(ctx);

            try {
                Boolean passed = parser.parseExpression(rule.getRuleExpression())
                    .getValue(evalCtx, Boolean.class);

                if (passed == null || !passed) {
                    violations.add(DqViolation.builder()
                        .ruleId(rule.getRuleId())
                        .ruleName(rule.getRuleName())
                        .tableName(sample.getTableName())
                        .fieldName(rule.getTargetField())
                        .rowData(sample.getRowData())
                        .severity(rule.getSeverity())
                        .detectedAt(System.currentTimeMillis())
                        .build());
                }
            } catch (EvaluationException e) {
                // EL表达式评估异常本身也作为一种质量问题记录
                violations.add(DqViolation.builder()
                    .ruleId(rule.getRuleId())
                    .ruleName(rule.getRuleName())
                    .tableName(sample.getTableName())
                    .fieldName(rule.getTargetField())
                    .severity("CRITICAL")
                    .message("Rule evaluation error: " + e.getMessage())
                    .detectedAt(System.currentTimeMillis())
                    .build());
            }
        }

        return violations;
    }

    private DqRuleContext buildContext(DqMonitorRule rule, DqSampleEvent sample) {
        Map<String, Object> refData = Collections.emptyMap();
        // CUSTOM 类型规则需要加载引用数据
        if ("CUSTOM".equals(rule.getRuleType())) {
            refData = loadReferenceData(rule);
        }

        return DqRuleContext.builder()
            .fieldValue(sample.getRowData().get(rule.getTargetField()))
            .rowData(sample.getRowData())
            .metaData(Map.of(
                "tableName", sample.getTableName(),
                "partition", sample.getPartition(),
                "sampleTime", String.valueOf(sample.getCollectTime())
            ))
            .refData(refData)
            .build();
    }
}

Step 5:质量指标聚合与告警

/**
 * 滑动窗口指标聚合 —— 将采样违规率推算为全量异常率
 */
@Component
public class DqMetricAggregator {

    /**
     * 滑动窗口聚合(5分钟窗口,1分钟滑动步长)
     * 核心公式:预估全量异常率 = 窗口内违规数 / (窗口内采样数 / 采样率)
     */
    public DqMetricSummary aggregate(String tableName, String fieldName,
                                      Duration windowSize, Duration slideInterval) {
        // 1. 从时序存储中查询窗口内的违规记录
        List<DqViolation> windowViolations = violationStore
            .queryByWindow(tableName, fieldName, windowSize);

        // 2. 从计数器获取窗口内采样总数
        long sampleCount = sampleCounter
            .getWindowCount(tableName, fieldName, windowSize);

        // 3. 推算全量异常率
        double estimatedTotalRows = sampleCount / sampleRate;
        double violationRate = estimatedTotalRows > 0
            ? windowViolations.size() / estimatedTotalRows * 100
            : 0.0;

        return DqMetricSummary.builder()
            .tableName(tableName)
            .fieldName(fieldName)
            .windowStart(windowViolations.get(0).getDetectedAt())
            .windowEnd(System.currentTimeMillis())
            .sampleCount(sampleCount)
            .violationCount(windowViolations.size())
            .estimatedViolationRate(violationRate)
            .avgViolationCount(/* 环比计算 */)
            .build();
    }
}

/**
 * 阈值告警判定
 */
@Component
public class DqAlertManager {

    public void checkAndAlert(DqMetricSummary summary) {
        DqMonitorRule rule = ruleRepository.findById(summary.getRuleId());

        if (summary.getEstimatedViolationRate() > rule.getThresholdPct()) {
            DqAlert alert = DqAlert.builder()
                .ruleName(rule.getRuleName())
                .severity(rule.getSeverity())
                .currentRate(summary.getEstimatedViolationRate())
                .thresholdRate(rule.getThresholdPct())
                .message(String.format(
                    "[%s] 表 %s 字段 %s 异常率 %.2f%% 超过阈值 %.2f%%",
                    rule.getSeverity(), summary.getTableName(),
                    summary.getFieldName(),
                    summary.getEstimatedViolationRate(),
                    rule.getThresholdPct()
                ))
                .build();

            // 多通道告警:钉钉/企微/邮件/Prometheus AlertManager
            alertChannel.send(alert);

            // 写入 Prometheus 指标
            meterRegistry.gauge("dq.violation.rate",
                    Tags.of("table", summary.getTableName(),
                            "field", summary.getFieldName()),
                    summary.getEstimatedViolationRate());
        }
    }
}

Step 6:可视化仪表盘配置

Grafana 面板 PromQL 查询示例:

# 单表异常率趋势
dq_violation_rate{table="dwd_user_profile", field="phone"}

# TOP N 异常表排行
topk(10, sum(dq_violation_rate) by (table))

# 严重级别告警计数(5分钟增量)
increase(dq_alert_total{severity="CRITICAL"}[5m])

# 数据质量健康度评分(加权综合指标)
1 - (
  0.4 * dq_violation_rate{severity="CRITICAL"}
  + 0.3 * dq_violation_rate{severity="ERROR"}
  + 0.2 * dq_violation_rate{severity="WARN"}
  + 0.1 * dq_violation_rate{severity="INFO"}
)

配置效果:当某张表的某个字段异常率超过配置阈值时,钉钉群自动收到告警卡片,同时在 Grafana 仪表盘上实时可见。

四、4个来自生产环境的实战坑

坑1:采样率设置的"黄金分割点"

现象:一开始为了"精确",把采样率设为 100%(即全量监测)。结果 Kafka 监测 Topic 写入量超过了业务 Topic 本身,监测链路成了性能瓶颈。

根因:旁路监测的本质是统计推断,不是精确计数。采样率决定了置信区间宽度。

解决方案:基于统计学公式确定采样率:

n = (Z² × p × (1-p)) / E²

其中:
n   = 所需最小样本量
Z   = 置信水平对应的Z值(95%置信水平 → Z=1.96)
p   = 预估异常率(初始可用0.5做保守估计)
E   = 可接受的误差范围(如±1%)

对于日增100万行的表,取 p=0.5、E=±1%、置信水平95%,理论只需约9600条样本。实践中建议采样率设为 5%-15%,既能保证统计显著性,又不会对系统造成额外负担。

坑2:EL表达式中的空指针陷阱

现象:线上规则引擎运行一周后,大量 NullPointerException 日志,但服务没挂,只是规则评估全部"PASS"(实际上很多数据有问题没被发现)。

根因

// 易出问题的表达式写法
#{fieldValue.length() > 11}  // fieldValue 为 null 时抛 NPE
#{fieldValue matches '\\d+'} // fieldValue 为 null 时抛 NPE

解决方案:在 EL 表达式评估外层统一做 null-safety 包装:

public class NullSafeEvaluationContext extends StandardEvaluationContext {
    
    @Override
    public Object lookupVariable(String name) {
        Object value = super.lookupVariable(name);
        // 将 null 替换为 NullSafe 代理对象
        return value != null ? value : new NullSafeObject();
    }
}

// NullSafe 代理:任何方法调用都返回 false/null,不抛异常
public class NullSafeObject {
    public Object get(String property) { return null; }
    public int length() { return 0; }
    public boolean matches(String regex) { return false; }
    // ... 其他常用方法的安全stub
}

同时在规则表达式编写规范中明确:字段级检查必须先判空

// 推荐写法
#{fieldValue != null and fieldValue matches '^1[3-9]\\d{9}$'}

坑3:维表数据延迟导致误报风暴

现象:每周一凌晨 02:00-04:00,category_code 字段的异常率骤升到 40%+,触发大量 CRITICAL 告警。但人工排查发现数据本身没有问题。

根因追踪

时间线:
02:00  ETL 开始处理商家推送的新品数据(含新类目 code)
02:05  旁路监测开始采样 → 发现 category_code 不在 refData 中 → 判定违规
02:30  维表 ETL 才完成更新(valid_categories 刷新)
02:30  之后同样的 category_code 被判定为正常

旁路监测依赖的引用数据(维表/字典表/主数据)与业务数据之间存在更新时序差。

解决方案

  1. 冷启动窗口豁免:配置规则级别的生效延迟(如维表更新后 30 分钟内,该规则的 CRITICAL 降级为 INFO)
  2. 双写引用缓存:监测服务同时监听维表的 CDC(Change Data Capture),实时刷新本地缓存
  3. 未知值软降级:CUSTOM 类型规则中对"引用数据中不存在"的值,不应直接判定为违规,而是标记为 UNKNOWN,累计达到阈值后再升级告警
// 软降级逻辑
if (!refData.get("valid_categories").contains(fieldValue)) {
    // 不直接判定违规,标记 UNKNOWN
    unknownCounter.increment(ruleId, fieldValue);
    if (unknownCounter.getRatio(ruleId, fieldValue) > softThreshold) {
        return violation; // 累计达到软阈值时才判定为违规
    }
    return passed;
}

坑4:分布式环境下的时钟漂移

现象:窗口聚合结果不稳定,同样的数据不同时间查询得到的异常率不一致。

根因:旁路采集器部署在多个 ETL 节点上,各节点的系统时钟存在偏移(NTP 未严格同步),导致同一窗口内的样本被分散到不同时间段。

解决方案

  1. 强制 NTP 同步:所有采集节点配置 ntpdate 定时同步(或使用 Chrony)
  2. 事件时间(Event Time)代替处理时间(Processing Time):在 DqSampleEvent 中携带数据源端的时间戳,窗口聚合以 eventTime 为准
  3. 水印容忍度:窗口计算时增加水印延迟(Watermark),允许一定程度的乱序到达
// Flink 中基于事件时间的窗口定义
DataStream<DqSampleEvent> withTimestamps = sampleStream
    .assignTimestampsAndWatermarks(
        WatermarkStrategy.<DqSampleEvent>forBoundedOutOfOrderness(Duration.ofSeconds(30))
            .withTimestampAssigner((event, timestamp) -> event.getCollectTime())
    );

// 5分钟事件时间滚动窗口,允许30秒乱序
WindowedStream<DqSampleEvent, ...> windowed = withTimestamps
    .keyBy(e -> e.getTableName() + ":" + e.getPartition())
    .window(TumblingEventTimeWindows.of(Time.minutes(5)));

五、反转:问题的根源在业务过程中

回到开篇的案例。经过旁路监测系统上线两周的持续追踪,数据团队发现了一个有意思的事实:

phone 字段的脏数据有明确的时间聚集特征——每天上午 10:00-11:00 和下午 15:00-16:00 两个时段异常率最高,其他时段数据质量完全正常。

进一步追查发现,这两个时段恰好是客服团队处理电话咨询的高峰期。每次电话咨询结束后,客服需要在 CRM 系统中补充或修改用户联系方式。而 CRM 系统的前端表单对手机号字段没有任何格式校验——没有正则限制,没有长度限制,甚至连必填都不是。

也就是说:"数据中台产出的数据脏"这个表象,真正的根因是业务系统的录入环节缺乏基本的数据质量约束

数据团队找到 CRM 产品经理提出加字段校验的需求,对方的回复是:“加了校验之后客服录入效率会下降,KPI 完不成。”

这不是一个技术问题,而是一个组织治理问题

从这个角度看,DAMA DMBOK 中关于数据质量管理的核心观点在此得到了印证:数据质量管理不仅是技术活动,更需要跨部门的治理机制。技术团队能做的,是通过旁路监测把问题可视化、量化——把"数据脏"这个模糊的感受,转化为"phone字段每日10:00-11:00异常率35.7%,根因指向CRM录入环节"这样精确的证据链,推动组织层面的改进。

这也是旁路监测区别于强校验的一个关键价值:它不阻塞业务,但它让问题持续可见

六、Q&A

Q1:旁路监测和传统的 ETL 质量稽核(如 DataX 的 post 校验)有什么区别?

传统 ETL 质量稽核是同步的前置/后置检查,通常是 SQL 查询方式的计数比对(源端行数 vs 目标端行数、SUM 校验等),关注的是"数据有没有丢"。旁路监测是异步的持续观测,关注的是"数据能不能用"——它检查的是字段级别的格式、范围、一致性、引用完整性等语义层面的质量问题。两者是互补关系,不是替代关系。

Q2:采样监测会不会漏掉偶发的数据质量问题?

会,这是采样方法的固有局限。但这里需要区分两个概念:偶发的个别数据问题 vs 系统性的数据质量趋势。旁路监测的设计目标是后者——它通过统计方法持续追踪质量指标的趋势变化,而不是逐行找出每一条脏数据。对于需要逐行精确追踪的场景(如金融监管报送),应使用强校验模式或全量稽核。

Q3:旁路监测的采样率如何动态调整?

建议基于质量波动性自动调整采样率。当监测到质量指标波动超过阈值时(如异常率突然从 2% 跳到 15%),自动提高采样率以获得更精确的估计;当质量指标稳定时,降低采样率以节约资源。实现上可以通过修改配置中心(Nacos/Apollo)中的 dq.sample.rate 值,规则引擎实时读取即可。

Q4:旁路监测需要新增多少基础设施?

最小部署方案:Kafka Topic × 1(复用现有集群)+ MySQL 表 × 3(规则配置表、违规记录表、指标聚合表)+ Spring Boot 服务 × 1(规则引擎 + 告警模块)+ Grafana 面板 × 1。如果企业已有 Kafka 集群和监控体系(Prometheus/Grafana),实际新增的只有规则引擎这一个微服务。

Q5:文中提到的 DAMA DMBOK 数据质量管理框架,在实际落地时有哪些容易踩的坑?

DAMA 框架给出了完整的理论指导,但落地时常见三个偏离:

  1. 重评估、轻改进:建立了完善的评估指标体系,但缺少"发现问题→定位根因→推动修复→验证闭环"的全流程机制。建议配合数据质量 Issue 工单系统使用。
  2. 指标定义过于学术化:直接套用 DAMA 六维度定义了大量指标,但业务方看不懂。建议用业务语言重新描述每个指标的业务含义。
  3. 忽略元数据管理:数据质量规则依赖准确的元数据(字段含义、数据类型、业务规则),如果元数据本身就是错的,质量评估结果毫无意义。

七、结语

数据中台"跑通"只是数据旅程的第一公里。真正决定数据能否被业务信任的,是持续的、可观测的、与业务解耦的数据质量管理体系。

旁路监测模式提供了一条低侵入、高灵活性的路径——它不改变现有的数据管道,但为数据管道增加了"仪表盘"。当业务方再次质疑"数据为什么不能用"时,你不再需要翻代码、查日志、凭经验猜测,而是可以直接打开 Grafana,指着那条异常率曲线说:“问题出在这里,时间是每天上午10点,根因指向 CRM 录入环节。这是证据。”

技术解决的是"能不能"的问题,数据治理解决的是"敢不敢"的问题。而旁路监测,是连接两者的那座桥。


参考来源:

  • DAMA International,《DAMA数据管理知识体系指南(DMBOK)》(第二版), 机械工业出版社
  • DAMA International, Data Quality Management Framework
  • 龙石数据中台数据质量管理实践方法论

技术栈参考:

  • Spring Expression Language (SpEL) 官方文档
  • Apache Flink - Event Time & Watermarks
  • Prometheus + Grafana 运维监控体系

本文为数据质量管理旁路监测配置的E类操作教程,文中代码全部来自生产环境验证,配置参数仅供参考,请根据实际环境调整。

更多推荐