开头先交代一句:这篇文章源自我的真实项目笔记整理,不是教科书式的架构科普。整篇内容围绕一个从零搭建的中型大数据平台项目展开,覆盖立项评估、集群规划、数仓构建、实时链路、质量保障、可视化大屏和面试复盘七个核心段落。如果你正在准备数据开发岗位的面试,或者刚接手一个大数据项目不知道从哪下手,这篇笔记应该能帮你把思路理顺。
我和团队做过好几个大数据项目,从早期的 Hadoop 生态到后来的实时数仓,踩过的坑和沉淀下来的方法都不少。这篇文章里的内容大概是2023年下半年到2024年上半年,我们做的一个电商数据分析平台项目的真实记录。平台本身不复杂,但麻雀虽小五脏俱全,涉及了离线数仓、实时计算、数据质量监控、数据大屏展示等完整链路。下面我按项目的实际推进顺序,把关键决策和实操过程写下来。
1. 立项阶段最容易忽略的事:先搞清楚数据从哪来、要到哪去
很多大数据项目失败,不是技术不行,而是需求阶段就没把数据的来龙去脉摸清楚。我们项目启动时,第一件事不是什么技术选型、集群规划,而是花了两周时间做数据源盘点。
1.1 数据源盘点怎么做才不漏项
电商平台的数据源大概分三类:业务库(MySQL、PostgreSQL)、日志文件(Nginx、App 埋点)、第三方接口(支付回调、物流状态)。我们当时做了一个数据源清单表格,每个数据源至少记录以下字段:
| 数据源 | 存储类型 | 数据量级(日增) | 更新频率 | 重要级别 | 对接负责人 |
|---|---|---|---|---|---|
| 订单库 | MySQL | 约 80GB | 实时写入 | P0 | 后端 A |
| 用户库 | MySQL | 约 5GB | 实时写入 | P0 | 后端 B |
| 埋点日志 | 文件 | 约 200GB | 每小时滚动 | P0 | 前端 C |
| 支付回调 | API 接口 | 约 10GB | 实时推送 | P1 | 后端 D |
这个表格是后面所有工作的基础。团队里经常出现的情况是:业务方说“数据都在库里”,但等你要同步时才发现库表结构天天变、没有更新时间字段、源库是跨机房访问的。这些坑都是盘点阶段能提前暴露的。
做盘点时有一个容易被忽略的细节:必须确认每个源表是否有主键或唯一键、是否有更新时间字段(modify_time)。这直接决定了后面用 Sqoop 还是 DataX、用增量同步还是全量同步。我们遇到过一个用户表,三年没加过 modify_time,全量同步跑了七个小时,还把业务库压垮了。后来是找 DBA 加了字段才解决。
1.2 数据量评估与算力预估的简易公式
从零做规划,数据量不要往大了吹,按未来半年的增长量估就够了。
估算公式大致是:
每日增量数据 = Σ(各业务表日新增行数 × 单行平均大小) + Σ(各日志文件日新增条数 × 单条日志平均大小) 全量存储需求 = 每日增量 × 存储周期 × 副本数 × (1 + 压缩率节省空间系数)我们当时的估算结果是:
- 每日新增原始数据约 300GB(订单+日志+接口)
- 计划存储周期:原始数据 180 天,清洗后数据 365 天,汇总数据永久保留
- HDFS 采用 3 副本,数据压缩(LZO)后约为原始大小的 40%
算下来整个集群需要的裸存储约 300GB × 180 × 3 × 0.4 ≈ 64TB。再加上 NameNode 元数据、临时计算空间等,最终配了 12 台物理机,每台 8TB 存储。这个量级后面跑起来基本没出现过磁盘告警。
提示:如果公司的数据规模每天不足 100GB,不建议一上来就上 ClickHouse、Doris 这类分析型数据库,直接用 Hive 数仓 + MySQL 报表就够了。很多团队犯的错是用导弹打蚊子,最后运维复杂度远超收益。
1.3 需求侧梳理:SLA 和数据消费方
盘点完数据源,下一步是梳理数据消费方。谁会用到这些数据?是实时大屏、报表系统、算法团队,还是老板的数据看板?不同消费方对数据的时效性、准确性要求完全不同。
我们总结了一个需求矩阵的简版:
| 数据产品 | 数据时效 | 允许延迟 | 数据准确性 | 下游 |
|---|---|---|---|---|
| 实时大屏 | 秒级 | ≤ 15 秒 | 高(99.9%) | 运营、管理层 |
| 经营日报 | T+1 | 次日上午 8 点 | 高(100%) | 财务、经营分析 |
| 个性化推荐 | 小时级 | ≤ 1 小时 | 中等 | 算法团队 |
| 临时取数 | 无要求 | 可容忍 2 小时 | 高 | 数据分析师 |
这个矩阵的价值在于:它决定了你要不要上实时链路、实时链路的可靠性要求有多高、离线任务必须在几点前跑完。如果所有需求都是 T+1,那就不用花大力气搞 Flink;如果大屏要求秒级准确,那就要考虑 Lambda 架构或者实时数仓的投入产出比。
2. 集群规划与部署:不追求豪华,追求够用和稳定
集群怎么搭,网上教程很多。这里我不讲标准安装步骤,重点分享几个真正影响后续稳定性的决策点。
2.1 硬件选型与组件版本组合
大数据组件版本兼容性是大坑,不要随便选最新版。我们最终使用的组合如下,跑了一年多没出过严重兼容问题:
| 组件 | 版本 | 说明 |
|---|---|---|
| JDK | 1.8.0_202 | 不要用高版本 JDK 跑 Hadoop 生态,坑多 |
| Hadoop | 3.3.4 | HDFS + YARN 一体 |
| ZooKeeper | 3.7.1 | 3 节点 |
| Hive | 3.1.3 | 元数据存 MySQL,使用 Tez 引擎 |
| Spark | 3.3.0 | 主要做离线清洗 |
| Flink | 1.16.2 | 实时计算,配合 Kafka |
| Kafka | 3.3.1 | 消息队列,3 节点 |
| DataX | 3.0 | 离线同步,阿里开源 |
| Doris | 2.0.3 | 实时 OLAP,兼报表查询 |
| DolphinScheduler | 3.1.8 | 任务调度,替代 Airflow |
硬件配置方面,我们用了 12 台 DataNode(同时承担 NodeManager),3 台 NameNode(其中 1 台 Active、1 台 Standby、1 台做备用的 QJM 节点),3 台 Kafka + ZooKeeper(复用)。每台机器内存 128GB,CPU 32 核,系统盘 480GB SSD,数据盘 8TB × 4。
注意:不要为了省钱把 NameNode 和 DataNode 混布在一个节点上。NameNode 的内存占用和 GC 问题会直接影响集群稳定性,尤其当日元数据量超过千万级文件时,混布会导致频繁 Full GC。
2.2 NameNode 元数据管理:最容易忽略的隐患
HDFS 的 NameNode 元数据是放在内存里的,文件数越多内存占用越大。我们的集群跑了大半年后,文件数涨到了 1.2 亿,NameNode 堆内存一度飙到 90GB。这个问题的触发点是小文件过多——上游 Flink 的 checkpoint 文件、Spark 的临时输出、Hive 分区表日增分区,都在不断制造小文件。
解决方案有三个层面:
- 合并小文件:离线任务输出时强制用
distribute by进行分区内合并,Hive 设置hive.merge.smallfiles.avgsize=268435456,把平均小文件大小合并到 256MB 以上。 - 定期清理临时文件:写脚本每天清理
/tmp、/user/hive/warehouse下超过 7 天的临时目录。 - 监控告警:对 NameNode 堆内存、文件总数设置监控,超过阈值的 80% 就告警,提前做扩容或清理。
这些事看起来很简单,但“不炸不修”的团队十有八九会在这里翻车。
2.3 YARN 资源调度的三种队列划分方案
YARN 资源分配不合适,集群就会出现“任务排队但 CPU 空闲”的奇怪现象。我们经过三次调整后采用了以下队列方案:
| 队列名 | 容量占比 | 最大资源 | 用途 | 优先级 |
|---|---|---|---|---|
| root.etl | 50% | 60% | 离线清洗、数仓构建 | 高 |
| root.realtime | 20% | 30% | Flink 实时任务 | 中 |
| root.adhoc | 15% | 20% | 临时查询 | 低 |
| root.default | 15% | 20% | 默认队列,未归类的任务 | 低 |
一点经验:Flink 实时任务如果和离线任务混在一个队列,很容易出现离线任务把集群资源占满,导致实时任务 checkpoint 超时失败。所以我们后来把 realtime 队列单独拎出来,并且配置了yarn.scheduler.capacity.maximum-am-resource-percent=0.4,防止 AM 占用过多资源。
特别提醒一下:跑数仓任务时尽量用 Spark 的spark.dynamicAllocation.enabled=true,同时配合队列容量限制,否则一个大的 ETL 任务会把整个集群的资源抢完,其他任务全部卡死。
3. 离线数仓的分层设计:每一层解决一个具体问题
数仓的分层设计看着是老生常谈,但真正能讲清楚每一层为什么要这么分的人不多。下面我用我们项目中的订单主题为例,把每层的职责和实现细节写清楚。
3.1 ODS、DWD、DWS、ADS 的定位误区
很长一段时间里我对分层停留在“分层就是为了好管理”这种模糊认知上。实际做完一个项目后,我理解每一层都有明确的职责:
- ODS(操作数据存储层):只做原样接入,保留完整的历史痕迹,不做任何业务清洗。它的作用是确保“源头可追溯”,出问题随时能回补。
- DWD(明细数据层):做清洗、去重、标准化、维度退化,形成业务过程的事实明细。核心是“一行代表一个业务事实”,多宽都行,但不能有重复。
- DWS(汇总数据层):按主题做轻度汇总,比如按天、按小时、按商品、按用户等维度预先聚合,减少重复计算。
- ADS(应用数据层):面向具体报表和应用的数据,宽表或者预计算的结果,尽可能保证查询性能。
用订单来举例。ODS 表里可能有多张表:ods_order_info(订单主表)、ods_order_item(订单明细)、ods_user_pay(支付流水)。DWD 层会把这些表通过订单号关联,清洗掉撤销单、测试订单、异常状态,形成一张dwd_order_detail分区表。DWS 层则按“天 + 商品 + 店铺”维度聚合出dws_sku_sales_1d这样的汇总表。ADS 层直接关联店铺维度、价格带维度,输出给报表系统。
3.2 维度建模与缓慢变化维的取舍
维度建模我们用的是 Kimball 的星型模型,没有搞复杂的雪花模型。事实表用事务事实表,一个订单对应一行;维度表只处理了用户维度、商品维度、店铺维度、时间维度。
缓变维度的处理值得单独说一下。比如用户所在城市,用户搬家后维度表更新,历史事实该怎么办?我们的处理策略是:
| 策略 | 适用场景 | 实现方式 |
|---|---|---|
| 拉链表 | 用户地址、会员等级 | 记录历史版本,用生效日期和失效日期 |
| 覆盖更新 | 与业务无关的修正 | UPDATE 原记录 |
| 新增行 | 需要留历史事实 | 新开一行记录 |
我们最终只对用户维度的关键属性做了拉链表(地址、等级),其他属性直接覆盖。做太多版本管理会显著增加查询复杂度,不值当。拉链表的 ETLSQL 其实不复杂,核心就是“上一版本失效 + 新增最新版本”,每天一个 20 分钟的增量任务就能搞定。
3.3 数据倾斜问题:SUM 都能算错?
数据倾斜是离线计算最容易踩的坑,也是大数据面试最高频的考点之一。我们遇到过一个典型的倾斜场景:统计 TOP 100 商品的销售额,商品维度的 join 时,一个大爆款商品的数据量占了全表的 60%,单个 Reduce 任务跑了 2 小时,其他 Reduce 几分钟就结束了。
当时做了三个优化:
- 大小表 Join 的 MapJoin:如果小表不超过 100MB,用
/*+ MAPJOIN(b) */提示,小表加载到内存里,大表在 Map 端直接关联,不走 Reduce。我们商品维表就一百多 MB,很适合这种方式。 - 热点 Key 加盐:对于无法避免倾斜的大表关联,给热点 Key 加随机前缀,把一条大 Key 拆分成多个子 Key 去计算,最后再合并结果。代码实现大概是把 join 条件改成
if (sku_id = '10001', concat(sku_id, '_', rand()%10), sku_id),两边保持一样的逻辑。 - 两阶段聚合:先按 key + 随机前缀做一次聚合,再去掉随机前缀做第二次聚合。经典写法,WordCount 升级版。
优化后那个任务从 2 小时降到 20 分钟。这类问题的排查工具就是看 Spark UI 的 Stage 耗时分布,有经验的工程师一眼就能判断是不是倾斜。
3.4 离线任务调度:DolphinScheduler 的几个实用配置
调度我们用 DolphinScheduler,替代了 Cron + Shell 的老方案。几个非常有用的配置分享下:
- 任务超时配置:每个工作流设置超时时间(比如 120 分钟),超时自动杀掉并告警。没有超时保护的任务挂起时非常耗资源。
- 上游依赖检测:用 Shell 任务去查询前一天的分区数据是否存在、是否有预期行数,不存在则直接失败并重跑。比如:
# 检查分区是否存在 hive -e "select count(*) from dws.dws_sku_sales_1d where dt='${yesterday}'" if [ $? -ne 0 ]; then exit 1; fi - 失败重试策略:重试次数设为 3,重试间隔 5 分钟。不要无限重试,否则遇到数据源故障会导致任务一直抢占资源,把整个集群拖垮。
事件先后顺序要严格:ODS 同步任务完成后再跑 DWD,DWD 完成后再跑 DWS。我们通过 DolphinScheduler 的 DAG 依赖关系解决了这个问题,再也不需要人肉等着看日志。
4. 实时链路的选择:什么时候该上 Flink,什么时候不该上
实时计算被吹得神乎其神,但真实场景里很多“实时需求”其实是“伪实时”。这里说说我们踩过的坑和最终的选择。
4.1 实时需求评估:用一份收益矩阵来决定要不要做实时
凡是业务方提“实时”需求,我都先问三个问题:你需要多准?你能容忍多少延迟?数据量是多大?
| 需求类型 | 延迟要求 | 数据量 | 建议方案 |
|---|---|---|---|
| 实时大屏 | 秒级 | 中等 | Kafka + Flink + Doris |
| 实时风控 | 毫秒级 | 大 | 单独做 Flink CEP 或专用引擎 |
| 会员积分变动 | 秒级 | 小 | 直接用服务端算 + Redis |
| 指标环比同比 | 分钟级 | 中 | Kafka + Flink 窗口聚合 |
当时业务方一上来就说要“实时经营报表”,要看到每一分钟的 GMV。后来我们拉了下数据量,每分钟订单只有几百条,实时和 T+1 的数据差距极小。最后我们用了折中方案:Kafka 同步业务 binlog 到 Doris,Doris 的 Aggregate 模型做分钟级聚合,完全满足需求,不需要上 Flink。
真正上 Flink 的只有两个场景:实时大屏和小时级个性化推荐的数据预处理。这两个场景数据量大、逻辑复杂,需要真正的流式计算。
4.2 Flink 实时任务的 checkpoint 与状态后端配置
一旦决定用 Flink,第一件事就是要把 checkpoint 调稳。我们遇到过不少“任务不报错但数据就是不对”的情况,绝大多数和 checkpoint 配置不当有关。一个比较实用的配置是:
execution.checkpointing.interval: 60s execution.checkpointing.min-pause: 30s execution.checkpointing.timeout: 10min state.backend: rocksdb state.checkpoints.dir: hdfs:///flink-checkpoints execution.checkpointing.externalized-checkpoint-cancel: RETAIN_ON_CANCELLATION几点说明:
state.backend: rocksdb适合状态大的任务,状态超过 1GB 不要用内存后端,GC 会把你搞疯。- checkpoint 间隔 60 秒、超时 10 分钟,保证不会频繁失败。
RETAIN_ON_CANCELLATION是任务停掉后保留 checkpoint 的关键配置,否则任务重启后从无状态开始,可能导致数据重复或丢失。
实时任务的监控也必须有。我们用的 Prometheus + Grafana 监控 Flink 的numRecordsInPerSecond、numRecordsOutPerSecond、checkpointDuration等指标。一旦吞吐量掉到历史均值的 50% 以下,或 checkpoint 失败次数超过 3,就触发告警。
4.3 实时数仓的写入链路:Kafka → Flink → Doris
实时写入 Doris 的方式是用 Flink Doris Connector,通过 Stream Load 协议批次写入。它的特点是延迟低(秒级)、吞吐高、支持去重。我们的链路大致是:
业务库 Binlog -> Canal -> Kafka(topic: ods_order_rt) -> Flink(清洗/维度关联) -> Doris(明细表/聚合表)注意几个细节:
- Kafka 分区数不要试图通过增加分区来提升消费吞吐,分区数超过 12 后收益很低,反而增加 ZooKeeper 压力。
- Flink 消费 Kafka 时,建议使用
setStartFromLatest()或setStartFromGroupOffsets(),不要每次从 earliest 消费,否则任务重启时会积压大量重放数据,可能压垮下游 Doris。 - Doris 写入的批次大小建议 10 万行或 20MB,超过这个阈值往往不是吞吐瓶颈,而是下游聚合和 Compaction 压力的来源。
Doris 的模型选择也要注意。明细类数据用 Unique 模型,带主键去重;聚合类指标用 Aggregate 模型,写入时直接 SUM/MAX/MIN。我们的大屏数据用的是 Aggregate 模型,字段是sku_id + date + hour的粒度聚合。
5. 数据质量保障:比算法更重要的工程底线
热搜词里有句话说得特别对:“对于大数据而言,最基本、最重要的要求就是减少错误、保证质量。”这句话从项目第一天起就要刻在团队文化里。数据质量有问题,再漂亮的报表都是废纸。
5.1 数据质量校验的“六性框架”
我们做了一套数据质量校验体系,主要分为六个维度:
| 维度 | 说明 | 检查方式 | 示例 |
|---|---|---|---|
| 完整性 | 数据是否有缺失、空值 | 非空校验、数量校验 | 订单表 id 不允许为空 |
| 唯一性 | 主键是否唯一 | 去重校验 | 订单号不重复 |
| 及时性 | 数据是否按预期时间到达 | 分区最大时间校验 | 昨天分区必须在 08:00 前就绪 |
| 有效性 | 数据是否符合业务规则 | 值域校验、枚举校验 | 订单状态必须是合法枚举 |
| 准确性 | 数据是否与源一致 | 总量比对、抽样比对 | 订单总额与源库一致 |
| 一致性 | 跨表、跨口径是否一致 | 指标交叉校验 | 当日成交额 = 订单 × 单价,两套口径对比 |
每个数据表在建表时就要配套一张质量校验规则表,每天调度跑完都执行校验,失败就触发布线任务。DolphinScheduler 里可以很方便地配置“数据质量”任务节点。
5.2 总量比对与抽样校验的实际案例
总量比对是最实用的一种校验。管道清洗完的数据,行数应该和源数据基本一致(有合理差值范围)。我们常见的一个校验 SQL 长这样:
-- ODS 层与源库行数比对 SELECT 'ods_order_info' AS table_name, count(1) AS target_cnt, (SELECT count(1) FROM source_db.order_info WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02') AS source_cnt, abs(count(1) - (SELECT count(1) FROM source_db.order_info WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02')) AS diff_cnt FROM ods.ods_order_info WHERE dt = '2024-01-01';如果diff_cnt / source_cnt > 0.1%,说明清洗逻辑有问题或同步丢失了数据,立刻告警并停止下游任务。这种做法虽然土,但比任何智能监控都可靠。
抽样比对则是随机抽取 1000 行明细,手工或脚本比对几个关键字段(金额、状态、用户ID)。我们在开发阶段每天做,上线后每周做一次。真实案例是:曾发现 ODS 层某个字段在同步时串列了,导致几天的数据全部错位,如果不是抽样比对,这个问题可能要在周报阶段才被发现,那时候的修复成本就高太多了。
5.3 数据血缘与影响分析
数据血缘是很多人都知道、但项目做到后期才知道其重要性的东西。我们用的是 Apache Atlas 来做血缘追踪(配合 Hive 元数据)。血缘的核心价值在于:当上游一张表字段变更时,能快速找出所有受影响的下游任务和报表,评估影响范围,做针对性的修改。
没有血缘管理的项目,出一次上游变更事故,就要全团队排查两天。有了血缘,一分钟定位到所有下游任务。尤其是 Flink 实时任务,往往清洗逻辑很复杂,一旦源表结构字段变了,实时任务不会立刻挂(反而会静默出错),血缘能帮你在第一时间发现。
6. 数据大屏的另一种思路:后端聚合与前端展示的配合
数据大屏是很多大数据项目的“门面”,也是老板最爱看的东西。热搜词里出现了“avue-data 数据大屏前端是怎么部署的”,说明不少人在纠结大屏技术选型。这里说说我的观点和实操方案。
6.1 大屏方案选型:开源组件、自研、商业产品怎么选
大屏技术方案有三条路:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 开源组件(DataV 开源版、avue-data、GoView) | 上手快、免费、图表丰富 | 定制化能力弱、性能遇到大数据量会卡 | 追求快速出效果、数据量不大 |
| 自研 ECharts + 前端框架(Vue/React) | 灵活、可控、可定制 | 开发量大、图表交互需要自己写 | 对效果和交互要求高 |
| 商业产品(帆软 BI、FineReport) | 功能完善、报表能力强 | 贵、黑盒 | 有预算且有复杂报表需求 |
我们选了自研 ECharts + Vue 的方案,原因有两个:一是大屏要展示的内容比较定制化(地图下钻、3D 翻牌、自定义旋转动画),开源组件模板改起来反而费劲;二是后端接口本身就能做聚合,前端不需要承载复杂逻辑。
6.2 后端聚合接口设计:一次查全量还是分批查
大屏数据接口的核心问题是:前端需要的数据往往不是明细,而是多维度的聚合结果。如果每次刷新都让前端逐个请求几十个指标,性能和体验都差。
我们的做法是设计一个聚合响应接口,一次返回大屏所有组件的数据:
{ "code": 0, "data": { "gmv_total": 12567890.00, "gmv_today": 356789.00, "order_cnt_today": 12345, "user_active_today": 67890, "top_sku": [ { "sku_name": "手机", "sales": 123456 }, { "sku_name": "电脑", "sales": 98765 } ], "hourly_trend": [ { "hour": "00:00", "gmv": 123 }, { "hour": "01:00", "gmv": 139 } ] } }后端处理逻辑分两步:
- 从 Doris 预聚合表查按小时、按主题聚合的数据,比如
dws_gmv_hourly_1d。 - 在服务端拼装成组件需要的数据结构,做字段名映射和格式转化。
这个接口用 Java Spring Boot 实现,只有约 200 行核心代码。每次大屏刷新走一次接口,响应时间稳定在 50ms 以内。不要直接在接口里跑 Hive 或 Spark,延迟不可控。
6.3 大屏前端部署的兼容性与性能优化
avue-data 这类组件在部署时有一个常见的坑:静态文件部署到 Nginx 后,WebSocket 或轮询接口要配好跨域(CORS),否则大屏能打开但数据不刷新。我们的自研方案没这个问题,因为接口和前端同域部署。
性能优化方面,大屏页面的几个关键点:
- 按需加载:ECharts 的按需引入非常重要,完整引入 ECharts 会导致首屏加载多 1MB 以上。我们只引入 LineChart、BarChart、MapChart、PieChart 这几个类型。
- 数据缓存:短时间内的轮询数据做缓存,比如 10 秒内的请求直接返回上次结果,减少后端压力。
- 定时刷新与 WebSocket 的取舍:数据变化频率低于 10 秒的,用轮询就够了;如果要求秒级同步,再考虑 WebSocket。大多数大屏的需求轮询完全能满足。
部署时我们把大屏项目构建为纯静态文件(Vue 打包后的 dist),部署在独立的 Nginx 服务上,反向代理配置/api路径转发到后端服务。整个大屏从开发到上线,两个前端工程师两周完成,后端接口开发加联调一周,成本可控。
7. 面试复盘:项目笔记里最有价值的部分
项目做完后最大的副产品,就是面试时可以把整个项目的技术决策和踩过的坑讲得很清楚。我个人认为面试官最看重的不是你用了多先进的技术栈,而是你对技术选型背后的权衡和教训有没有深入的理解。
7.1 高频追问:数据倾斜、小文件、拉宽表
技术面试时,几乎必问的问题集中在几个方向:
问题一:数据倾斜你是怎么发现和解决的?
这是考察频率最高的问题。我的回答会按“发现-定位-解决-预防”四步展开:发现靠 Spark UI 看 Stage 耗时;定位靠看数据分布,找出 Key 占比异常高的;解决手段看具体场景,是 MapJoin 还是加盐,还是两阶段聚合;预防靠平时的数据探查,对大 Key 提前做处理。把 3.3 节的实际优化过程讲一遍,面试官基本会满意。
问题二:数仓怎么处理小文件问题?
这个问题考察对 HDFS 元数据压力的理解。我们的方案是:
- 减少源头产生小文件:Flink 的 checkpoint 文件不要写入数仓 ODS,独立目录存储。
- 定期合并小文件:对 Hive 表执行
INSERT OVERWRITE TABLE xxx SELECT * FROM xxx DISTRIBUTE BY rand(),把小文件合并成大文件。 - 控制分区大小:
hive.exec.dynamic.partition=true时注意手动合并分区,防止每小时的 Tiny 分区产生过多文件。 - 关注 NameNode 内存和文件总数监控,提前发现风险。
问题三:宽表和窄表怎么选?宽表怎么拉?
我的经验是:分析场景用宽表,统计加工场景用窄表。宽表拉取就是把星型模型的维表属性退化到事实表,一般在 DWD 层做。建宽表时注意字段冗余度要可控,不是所有维度都退化进去,只退化那些高频使用的维度和枚举值。否则一张宽表一两百个字段,开发和维护都很难受。
7.2 项目笔记应该记录什么:一句话的提炼才是精华
最后分享一个关于“项目笔记”本身的心得。很多人记录项目就是记流水账:今天部署了 Flink,明天写了一个 SQL。这种笔记没什么用。我记录项目笔记的原则是:每个模块只记三件事——为什么这么做、怎么做的、踩了什么坑。
举个例:
2024-01-15:订单实时指标采用 Doris Aggregate 模型,而不是 Flink 聚合后写 MySQL。原因:大屏的查询模式是多维度 ad-hoc,Doris 可以满足秒级查询且支持任意维度组合;Flink 的聚合结果相对固化,不够灵活。踩坑:Doris 聚合模型对 REPLACE 字段的更新有严格顺序要求,同一批数据重复导入会导致结果不稳定,后来通过在 Flink 侧统一按业务主键去重解决了。
这样一段笔记,面试时可以直接变成很好的技术交流素材。面试官听到的不是“我用了 Doris”,而是“我为什么用 Doris,以及遇到什么坑、怎么解决的”,这才是资深工程师和初级工程师的分水岭。
7.3 给正在做大数据项目的你三个诚恳建议
项目收尾阶段,结合我和团队的实际感受,给读者三个建议:
第一,技术选型一定要回到资源和业务成熟度上。不要因为某个开源项目热门就强行引入。我们的集群规模其实并不大,但通过合理的队列划分、数据分层和计算引擎选择,十二台机器做到了离线加实时混合负载,而且稳定运行了大半年。
第二,数据质量监控的优先级高于功能开发。很多团队把大屏、报表做得花团锦簇,但底层数据经常延迟或出错。功能再漂亮,用户问的第一句永远是“这个数据准不准”。从项目第一天开始搭建质量监控体系,后面能省无数事。
第三,用文档记录代替口头沟通。每次任务调优、集群改动、数据结构变更都记录下来,不管是对自己还是对后来接手的同事都是宝贵的资产。我这份笔记,最初就是随手记在本地,后来整理成体系后才真正发挥价值——既帮我理清了项目得失,也让面试时的表达有了清晰的主线。
大概就是这样。大数据项目没有业界公认的标准答案,每个团队都是在摸索中前进。希望这份笔记能给你提供一些可以复用的经验,少走几步弯路。如果你在项目里遇到过类似的问题,或者有更好的解法,欢迎交流讨论。