概览

数仓分层

参考链接:

数仓原理

数仓分层

数仓分层是业界公认的最佳实践:

  • 解耦数据处理流程。通过分层,可避免牵一发而动全身问题,修改某一层任务不影响上下游任务
  • 提升数据质量。在 dwd 层建立一致性维度原子事实,避免同一指标计算口径不一致
  • 增强可维护性
  • 保障数据一致性
  • 最终为上层应用提供稳定、高效的数据服务

ods、cdm(dwd、dim、dws)、ads

事实表。什么是事实,基本是数值

事实指的是业务过程中可度量、可量化、可分析的数值型指标或度量值(Metrics/Measures)

事实分类:

  • 可加事实:⾦额、数量,任何维度都可以进⾏累加
  • 半可加事实:某些维度下可以累加,其他维度下没意义,⽐如商品库存数量,当⽇库存有意义,⼏天的库存加⼀起就没意义
  • 不可加事实:⽐率之类的,累加后没意义。任何维度下都不能累加,这类指标通常需要基于可加事实计算得出。

维度建模(如星型模型、雪花模型)中,事实被集中存储在事实表中。事实表通常位于模型的中心,周围环绕着提供上下文的维度表。

事实表的结构:

  • 主键。
  • 外键:指向各个维度表的键,用于关联维度信息。如product_idcustomer_id
  • 事实列:如 sales_amount, quantity_sold, page_views

事实表分类:

  • 事务事实表。单业务过程
  • 周期快照事实表。单业务过程
  • 累积快照事实表。多业务过程,多个时间节点
  • 没有事实的事实表。比如日志或埋点,只有时间和行为,没有具体的事实。可基于这类表统计出⼈数、频次事实指标

如何建⽴⼀张事实表

  1. 确认业务过程 → 确认数据粒度 → 确认维度 → 确认事实 → 退化维度属性

维度表

如何建⽴⼀张维度表

缓慢变化维(SCD)

层级维度表如类目、组织架构维度处理。一般是打平,如类目有固定层级 3 级或 4 级,组织架构一般不会有层级限制,可无限创建,但实际应用中也就 5 层左右。

维度建模

维度建模方式:星型模型、雪花模型、星座模型

在数据仓库的维度建模中,星型模型雪花模型 是两种最核心的模式设计。它们都基于事实表 + 维度表的架构,但在维度表的规范化程度上存在关键区别:

  • 星型模型。维度表是非规范化的,维度表通常是宽表,包含丰富的层次信息,且不进行拆分
    • 优点。查询性能高,只需要事实表 join 维度表即可获得结果,甚至对事实表进行维度退化冗余维度可避免 join。在大数据中一般认为计算资源比存储资源更昂贵,通过冗余存储可减少计算时 join 资源消耗
    • 缺点。数据冗余,存在潜在数据一致性风险。如类目名称在多条商品记录中重复存储,当需要修改类目名称时需全部修改
  • 雪花模型。维度表时规范化的,维度表不是宽表,而是根据范式原则被拆分成多个相关的表,形成层级结构
    • 优点。减少冗余,数据一致性好,层级关系清晰
    • 缺点。
      • 查询性能低,需多次 join 维度
      • 复杂性高,需理解模型结构中维度表设计

如何选择?推荐采用星型模型:数仓需满足分析需求查询性能易用性比存储资源和数据一致性更重要。

不同分层建模:

  • ods
  • dwd。
    • 存储方案
      • 每日存储所有数据量。如果每日新增数据在 1 亿以内可以考虑这种方式,以辛选订单表为例,20亿分销订单,每个分区存储全量数据,总存储在 3PB
      • 存储近期数据+历史数据。如存储近 1 年订单,当使用全部订单时需要和历史数据进行 union。当超出 1 年的数据还在变动时会出问题
      • 存储活跃数据+历史数据。和业务确定数据生命周期,当数据已不在变更时,不在存储。如订单数据,在超出 90 天后即不会变动,因此可只存储还会发生变动的订单。缺点是需要确定数据生命周期终点条件
  • dim
  • dws。日期+维度
    • 日期 + 指定维度
    • 日期+所有维度。
    • 竖表。日期 + 指标类型 + 指标结果。竖表需转化为横表才和方便使用

cube、group by sets、rollup

数仓开发流程

数据仓库的开发是一个系统化、分阶段的工程过程,旨在将分散、异构的源数据转化为结构化、高质量、可分析的决策支持信息。一个典型的开发流程遵循分层架构生命周期管理原则,主要包含几个关键阶段:

  • 需求分析。确定为什么建仓、为谁建仓、建什么
    • 业务需求调研。
      • 分析需求。确定业务部门需求,是想创建公司经营看板,还是想做交易对账、财务结算(业财一体),提供商业数据服务
      • 确定业务过程。如订单履约、用户注册、广告投放
    • 确定关键指标
      • 数据粒度
      • 数据来源
      • 数据及时性。T + 1,小时级,分钟级,秒/毫秒级
  • 架构选型
    • 离线 or 实时
  • 数仓建设
    • 模型设计。采用维度建模,设计星型模型雪花模型,确定事实表(事务、周期快照、累积快照)和维度表(退化维度、缓慢变化维度 SCD)
    • 分层架构。
      • ods。保留历史快照,支持数据溯源
      • cdm。
        • dwd。进行数据清洗、标准化、维度退化,构建原子粒度的事实表
        • dim。集中管理所有维度表(如时间、地区、产品),确保维度一致性
        • dws。基于 dwd 层,按主题(如用户、商品、流量)进行轻度汇总,生成公共的、可复用的宽表或汇总表
      • ads。
    • 数据开发(ETL/ELT)
      • 数据抽取
        • 全量抽取
        • 增量抽取
      • 数据转换
        • 清洗:处理脏数据(空值、异常值、格式错误)。
        • 标准化:统一编码、单位、命名规范(如“北京”、“北京市”统一为“北京市”)。
        • 脱敏:对敏感信息(如身份证、手机号)进行加密或掩码。
        • 关联:将不同来源的数据通过主键/外键关联(如订单表关联用户表)。
        • 计算:生成派生指标(如利润率 = (销售额 - 成本) / 销售额)。
        • 维度退化:将常用维度属性直接加入事实表,减少 JOIN。
        • 处理缓慢变化维度(SCD):管理维度属性的历史变化(如用户地址变更)。
      • 数据加载
        • 覆盖写入(Overwrite)
        • 追加写入(Append)
        • 合并写入(Merge/UPSERT)
    • 任务调度与运维
      • 调度配置
        • 设置任务的执行周期
        • 设置任务的上游依赖
      • 数据质量监控
        • 完整性、一致性、准确性、及时性
    • 监控告警
      • 配置告警和联系人
    • 数据服务。加工好的数据提供给下游使用
      • 接口开发
      • 报表开发
      • 数据回流
    • 持续迭代与优化
      • 需求变更响应
      • 任务优化
        • 性能优化
        • 存储优化

数据治理

存储资源治理

  • 目的
    • 减少存储资源的费用
    • 提高任务稳定性
  • 治理方式
    • 小文件治理。对于小文件分区,确定分区产生原因,重写分区,减少小文件分区
    • 表、分区、历史快照。
      • 对于无用表、分区、历史快照,进行删除
      • 对于使用频率低(偶尔使用、临时需求)或历史数据,通过归档、备份、迁移到低成本存储,进行冷热分离

计算资源治理

  • CU 资源。确定实时、离线任务使用的资源,评估任务资源是否合理
  • 治理方式。
    • 对于大资源任务,可尝试进行任务 SQL 优化或业务逻辑优化,减少任务资源、提高运行效率
    • 对于长耗时任务,可尝试进行计算逻辑优化,提前过滤数据或先分桶在合桶或处理数据倾斜,提高任务运行效率
    • 实时、离线混部,对于潮汐型的离线任务资源,可通过混部提高离线集群资源利用率

大数据原理

分区、分桶、索引、LSM

  • 分区
    • 固定分区,动态分区(自动创建分区、分区 TTL、分区命名和格式)
    • doris。
      • 分区类型。list 、range
      • 分区函数。无
    • paimon
  • 分桶
    • doris。分桶函数:hash、random
    • paimon。分桶函数:default(hash)、mod
  • 索引
    • 布隆过滤器索引
    • bitmap索引
    • 前缀索引。doris 内部自动创建的
    • 倒排索引。
  • z-order

常见问题

1.hdfs 小文件问题

  • 描述

    • 小文件。文件大小低于十几mb的文件。
    • 小文件问题就是 hdfs 中有大量的小文件,小文件数量很多。
  • 原因。hdfs 的设计是一次写入,多次读取大文件处理。任何违背这一原则的、产生大量小规模、零散写入的操作,都极有可能导致小文件问题

    • 计算引擎作业参数不合理
      • 离线。输出阶段的配置错误是导致小文件的常见原因
        • Hive。
          • 动态分区插入。动态分区插入时会为每个分区值组合创建一个目录,并在该目录下生成文件。
          • 数据分布不均匀。部分分区数据量很小
          • 分区键基数高。分区多而分区内数据量少
        • MapReduce。
          • reducer 参数设置不合理。⼀个 reducer 对应⼀个⽂件,如果 reducer 数量过多(如 set mapreduce.job.reduces=1000),即使输入是大文件,输出也会被分成 1000 个文件,导致输出文件变小。
        • Spark。
          • Spark的 repartition()coalesce() 操作分区数设置过大,saveAsTextFile()等操作会为每个分区生成一个输出文件。例如:df.repartition(1000).write.parquet("path") 会生成最多 1000 个文件,如果数据量只有 1GB,平均每个文件仅 1MB
      • 实时。⼩批量多次写⼊,又缺少压缩合并(compaction)机制
        • Flink。
          • 频繁提交。flink 在流式写入 hdfs 时往往采用流式写入、定时批次提交。如数据会先 buffer 在 writer 中,当 flink 做 checkpoint 或者 buffer 的数据量大小达到 64mb 或超过 10000 条或超过 10s 进行提交。如果参数配置不合理(依据具体的 writer 实现)会造成 flink 每次向 hdfs 写入时数据量较少
        • Spark Streaming
          • 类似 flink 的问题
    • 数据源本⾝是⼩⽂件
    • ETL 增量更新调度问题。一些 ETL 流程是增量执行的,每次只处理新增的数据并直接写入目标目录,没有一个定期合并任务处理增量数据
  • 影响

    • hdfs namenode 压力。
      • OOM 风险。namenode 将整个文件系统的元数据(包括文件名、权限、块位置等)存储在内存中。每个文件、目录和块在 namenode 中都对应一个对象,占有⼀定的字节记录元数据,小文件过多代表同等的存储资源下会产生更多的 namenode 元数据。
      • 影响数据备份和恢复。namenode 使用 fsimageeditlog 存储元数据,元数据过多会导致 fsimage 过大,备份和恢复耗时长。
    • MapReduce 任务运⾏效率低下。通常⼀个⼩⽂件会对应⼀个 map任务,⼀个 map 任务⼜会启动⼀个 jvm 虚拟机,在执⾏时候⽐较浪费资源
  • 解决办法。

    • 原理

      • 数据写入前进行 buffer 和 compaction。确保数据在每次写入时都足够多
      • 定期 compaction。如增量 ETL 任务,需定期合并增量数据和历史数据
    • 具体方法

      • 监控与告警。建立对 hdfs 文件数量、namenode 内存使用率的监控,及时发现小文件堆积问题

      • 计算引擎。调整配置,优化数据写入逻辑(增加 buffer 和 compaction 小文件变大文件)

        • 减少任务 reduce 数量。set mapred.reduce.tasks = 10

        • 增加⼀层任务重写分区数据,合并⼩⽂件

        • 通过调整参数解决⼩⽂件

          • set hive.merge.mapfiles=true,在map only任务中合并⼩⽂件

          • set hive.merge.mapredfiles=true,在reduce结束后启动⼀个map only任务

            合并⼩⽂件,通常配合 hive.merge.smallfiles.avgsize = 16000000 (16M) 参

          数⼀起使⽤

          • set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat。在map输⼊端进⾏⼩⽂件合并
      • 调整数据存储格式。优先选择支持块压缩和列式存储的格式,如 ParquetORC。这些格式本身可以将多个记录高效地存储在一个文件中,并且支持谓词下推,减少 I/O。

      • hdfs 本身

        • 调整 hdfs 块大小。对于已知会产生小文件的目录,可以将其块大小设置得更小(如32MB),但这通常不是好办法,因为会降低大文件的读写效率
        • 调整 JVM 参数,优化 GC 策略,提高 namenode 能力

2.数据倾斜的处理办法

  • 描述。在计算引擎中,大数据计算往往是分布式并行计算,数据倾斜指数据在各个计算节点(或分区)之间分布不均,导致部分节点处理的数据量远大于其他节点(或分区),从而成为整个作业的瓶颈,使得大部分节点(或分区)早早完成,而少数节点(或分区)长时间运行,严重拖慢整体作业进度。
  • 定位。如何定位发生数据倾斜
    • 执行耗时。对于长耗时任务或卡在某个阶段需检查一下任务是否发生数据倾斜,可查看 task 具体的执行耗时,如果有部分 task 执行耗时明显异于其他 task,则存在数据倾斜可能
    • OOM、磁盘 IO、网络 IO。如果发现个别集群节点存在 OOM 或磁盘 IO、网络 IO 特别高,可查看是否是数据倾斜引起的。数据倾斜情况下,部分集群节点处理数据量大,可能会发生 OOM、磁盘 IO、网络 IO 特别高
    • 数据统计。对数据进行全局或采样统计,统计 key 的分布情况
  • 原因
    • 热点 key。某个或某几个 key 数据量大
    • 聚合操作(group by)。部分字段值出现频率特别高(甚至是 null 或空字符串等异常值比较高)
    • 分区策略。分区类型或分区函数不合理,导致不同分区数据不均衡
  • 影响
    • 任务耗时长。数据倾斜导致分配给任务的资源未得到充分利用,任务处理慢耗时长,导致数据处理延迟
    • 任务不稳定。数据倾斜导致部分节点处理数据量大,导致节点异常,从而任务失败或重试
  • 解决办法
    • 热点 key。
      • 检查是否存在优化业务逻辑的可能。如热点 key 为默认值的情况
      • 打散热点 key。先分桶在合桶
        • 加盐。对热点 key 增加随机前缀或后缀,使用加盐 key 进行 group by 进行聚合计算时,先计算加盐 key 聚合结果,在去掉盐值按照原始 key 进行最终聚合
    • 过滤脏数据。避免 group by 时 null 值数据量很大

3.MapReduce运行过程

参考:

4. hdfs 读写流程

参考:

5.SQL 优化

  • SQL 执行计划
    • 查看
      • mysql。explain
      • hive。执行计划
      • flink。算子 DAG
    • 分析
      • 索引
      • 分区裁剪
      • 谓词下推
      • 统计信息。是否准确?是否因为统计信息不准确,计算引擎确定执行计划时出现误判
  • 优化
    • 索引
      • 新增/修改索引
      • 强行指定索引
    • JOIN。
      • 大小表 join 问题
        • 谁在前的问题。小表 join 大表,还是大表 join 小表
        • 大小表 join 方式。broadcast join,是否可以通过 hint 指定计算引擎或存储引擎的 join 实现
      • 大表和大表 join
        • SQL拆分,多阶段执行,中间引入中间表。
        • 分区裁剪,只读取必要的字段减少IO
        • 业务逻辑优化,避免大表 join
        • 加资源或者使用独享资源。无法避免大表和大表 join,需要加资源解决,同时避免数据重复计算,将大表和大表 join 结果写入中间表,后续任务直接使用中间表数据
      • 业务关联逻辑是否合理如产生了比较大的笛卡尔积
    • 数据倾斜
    • 复杂SQL,长耗时SQL
      • 对 SQL 拆分,多阶段执行,中间引入中间表。
    • 建表 DDL
      • 表分区和分桶
      • 索引
      • 主键
      • 存储格式。行存、列存、行列共存
      • 数据格式。orc、parquet、avro
      • 压缩方式
  • 执行计划生成
    • RBO。基于规则的执行计划,如大表和小表谁在前的问题,会导致执行计划和执行效率不一样
    • CBO。基于代价的执行计划
    • HBO。基于运行历史的执行计划。