一、如何实现数据的历史追踪
在数据仓库中实现数据的历史追踪主要是为了记录数据的变化轨迹,使得数据分析和审计能够追溯到历史记录。常见的实现方式包括:
- 快照表: 每个时间点的数据快照被保存为单独的表,这样可以精确追踪每个时间点的状态,但代价是数据冗余较高,一般用于财务报告等需要精确历史记录的场景
- 拉链表:通过记录每一条数据的有效时间段来实现版本管理,适合数据量不大但变更频率较高的场景
- SCD 缓慢维度变化设计: 见下文
SCD(Slowly Changing Dimension) 缓慢变化维度
通过将维度表设计为缓慢变化的维度,实现数据的历史追踪,主要有以下几类:
类型 | 应用场景 | 优点 | 缺点 | 实现方案 |
---|---|---|---|---|
覆盖更新(SCD1) | 不需要历史记录,如用户信息更新 | 简单,存储少 | 无法追溯历史 | |
新增记录(SCD2) | 需要完整历史,如产品价格变更 | 完整记录变化历史 | 存储开销大 | 拉链表 |
添加字段(SCD3) | 需要部分历史,保留当前值和上一个历史值,如客户地址变更,只需保留最近一次的历史记录 | 保留部分历史,存储适中 | 不适合频繁变化数据 | |
混合型(SCD6) | 不同属性需要不同历史追踪策略的场景,如用户信息管理(基本信息+重要属性) | 灵活性强,可按字段特性选择策略,查询性能比较平衡 | 实现复杂且维护成本高,需要清晰的字段分类 |
SCD6 和拉链表的对比:
- 拉链表:SCD2 的一种实现方案,每次数据变更都会产生新记录,存储空间消耗较大
- SCD6:结合了 SCD1(覆盖更新)、SCD2(新增记录)和 SCD3(保留历史字段)的特点,更加灵活可以选择性地决定哪些字段需要完整历史,哪些只需要保留最近一次变更,存储更有效率:
- 不需要历史记录的字段:使用覆盖更新策略,不产生新纪录
- 需要完整历史的字段:使用类似拉链表的机制
- 需要部分历史的字段:更新当前记录,将旧值移动到历史字段, 记录变更时间
二、数据倾斜的处理
主要是在数据 ETL,数据计算过程中出现,解决思路如下:
- 查看数据倾斜发生的地方: 一般是写 SQL 对数据分布进行分析,如果是 Spark 任务,可以通过 Spark UI 分析,更方便
- 如果是因为重复数据太多,则进一步做数据清洗,保留必要的数据
- 如果是因为表太大,则考虑拆分表
- 如果只是因为部分热点数据分布不均匀,则可以考虑使用随机数分区,在数据字段上添加随机数或哈希值,将热点数据打散到不同的节点上进行处理
扩展参考:
三、ETL 方案设计
主要问题:
-
如何保证数据延迟
比如常见场景下是从 mysql 做 etl 同步到数仓,因为需要保证不影响业务,是禁止直接抽取生产库或者主库数据的,通常是基于 binlog 或者从库抽取数据,为了保证数据不延迟,就需要做主从延迟监控,确保抽取的数据不落后太多
-
如何保证数据不缺失和重复
通常抽取原始数据时不会直接存储到数仓生产库,一般是另外建立一个快照库,增量数据更新iu 数据时,先写入快照库,然后做一次数据校验比对,确保数据正确再写入生产库
-
数据回滚和恢复
ETL 任务分阶段进行,任何一个阶段出现问题后或者任务异常中断都会触发回滚策略并删除无效数据。具体策略如下:
a. 数据备份机制
- 每次 ETL 任务执行前,对目标表做快照备份
- 对于增量更新场景,记录所有变更的数据版本
- 关键数据表保留多个历史版本的备份
b. 事务控制
- 采用分布式事务确保数据一致性
- 大批量数据处理时,按照业务划分为多个小事务批次
- 记录每个事务批次的状态,支持断点续传
c. 回滚策略
- 任务失败立即触发回滚机制
- 使用临时表进行数据处理,验证无误后再替换生产表
- 保留回滚日志,记录所有操作步骤
- 支持按时间点、版本号进行回滚
d. 数据恢复方案
- 建立数据血缘关系,追踪数据来源
- 支持增量/全量两种恢复模式
- 恢复过程中进行数据一致性校验
- 设置恢复优先级,确保核心业务优先恢复
e. 监控告警
- 实时监控任务执行状态
- 异常情况及时告警并自动触发回滚
- 记录详细的操作日志,便于问题定位
- 设置任务超时机制,避免任务长时间阻塞
四、数据归档和数据删除策略
- 确定数据在数据仓库中的保留周期,编写脚本任务定期将数据归档至云存储以及删除过期数据。
- 删除策略与合规性: 所有删除动作都是可逆的,比如通过将数据移动到暂存区一段时间后再进行永久删除
- 定期生成数据快照,以便在需要的时候可以进行数据恢复和追踪
- 日志审计:所有涉及到表数据变更的操作都需要记录在审计日志中,以便追溯和审计
五、枚举表更新规范与流程
业务中,枚举表通常用于存储一些固定的值,如状态码、类别、类型等,这些值在数据库中作为外键引用,供其他表使用。这些枚举表一般不需要频繁更新,但一旦发生变化,可能会导致对依赖这些枚举的其他表产生影响,甚至可能破坏数据的完整性,因此也需要制定相应的规范流程:
枚举表的更新规范
-
版本管理
- 每次更新时,都应该记录一个版本号,以便追踪更新历史,同时避免因不同版本任务因枚举值不一致导致的问题
- 枚举表包括一列用于标记记录生效日期
start_date
和失效日期end_date
的字段或者使用状态字段status
来标记该枚举值的有效状态,更新枚举值时同步更新记录
-
审批流程: 枚举表的更新必须通过业务方或相关人员确认和批准
-
软删除: 不直接删除旧的枚举值,而是通过更新失效日期或者添加状态字段来标记该枚举值已经不再有效
依赖枚举表的其他表规范
- 外键约束: 应当为每个枚举值提供一个标识符(ID),并将其作为外键引用。这样可以避免直接使用枚举值,而且当枚举表的值变更或删除时,可以通过外键约束阻止非法操作
- 枚举值的有效性检查: 定期检查引用枚举表的字段,确保其所有的枚举值都是有效的。在数据清理和迁移过程中,及时发现和修复不再有效的枚举值
- 订阅枚举值变更消息通知(比如依赖 Kafka 事件通知),在枚举表更新时,自动触发依赖表的数据更新
通知与回滚恢复机制
- 在枚举表更新时,通过通知机制(如邮件、短信、Slack 消息等)告知相关人员,特别是下游系统的开发人员,及时了解枚举表的变动,确保他们能做好相关的业务调整
- 为每次枚举表的变动生成详细的日志记录,包括变动的具体内容、变动时间、受影响的下游表等信息,帮助后续的故障排查和审计
- 配置备份枚举表,在枚举表更新时,将旧的枚举表备份,以便在需要时进行回滚恢复