数据密集型应用系统设计-批处理系统
MapReduce:批处理的基石
MapReduce 的核心思想
-
Map 阶段:将输入数据拆分为多个分片(Split),并行处理生成中间键值对(Key-Value Pairs)。
-
Shuffle 阶段:隐式阶段,负责将 Map 输出排序、分组并传输到 Reduce 节点。
-
Reduce 阶段:对中间结果按 Key 分组,进行聚合或转换,输出最终结果。
-
编程模型
# Map 函数:处理输入,生成中间键值对 def map(key, value): for item in process(value): yield (intermediate_key, intermediate_value) # Reduce 函数:聚合相同 Key 的值 def reduce(key, values): result = aggregate(values) yield (key, result)
MapReduce 执行流程
-
输入分片(Input Splits)
- 数据存储于分布式文件系统(如 HDFS),按固定大小(如 128MB)分片。
- 每个分片启动一个 Map 任务,由框架自动调度到集群节点。
-
Map 阶段
- 读取输入:从 HDFS 读取分片数据(如文本文件的一行)。
- 执行 Map 函数:用户自定义逻辑处理数据,输出中间键值对。
- 写入本地磁盘:Map 结果按 Key 分区(Partition)后写入节点本地磁盘(非 HDFS)。
-
Shuffle 与排序
- Fetch 阶段:Reduce 任务从所有 Map 节点拉取对应分区的数据。
- 排序与合并:拉取的数据按 Key 排序,合并相同 Key 的值列表。
-
Reduce 阶段
- 执行 Reduce 函数:对排序后的键值对进行聚合(如求和、去重)。
- 输出结果:结果写入 HDFS,每个 Reduce 任务生成一个输出文件。
MapReduce 的关键机制
-
容错与恢复
-
Task 失败:
Map/Reduce 任务失败时,框架重新调度到其他节点执行
已完成的 Map 任务需重新执行(因中间结果存储在失败节点的本地磁盘)。
-
节点故障:通过心跳检测(Heartbeat)发现宕机节点,重新分配其任务。
-
-
数据本地性优化
- 将 Map 任务调度到存储输入分片的节点,避免跨网络读取数据。
- 若本地节点繁忙,选择同一机架内的节点(机架感知策略)。
-
Combiner 优化
- 局部聚合:在 Map 阶段后、Shuffle 前,对中间结果进行预聚合(类似 Reduce)。
- 减少数据传输:例如 WordCount 中,Map 节点先合并相同单词的计数。
现代批处理框架
Apache Spark
-
内存计算:
- 将中间数据缓存到内存(RDD),减少磁盘I/O。
- 相比MapReduce,迭代算法性能提升10-100倍。
-
DAG执行引擎:
- 将作业拆分为有向无环图(DAG),优化任务调度。
- 支持多阶段任务合并(如Map后直接Reduce,跳过Shuffle)。
-
统一API:支持批处理(Spark Core)、流处理(Spark Streaming)、机器学习(MLlib)。
执行引擎优化
- 向量化处理:使用SIMD指令批量处理数据(如Apache Arrow内存格式)。
- 动态资源分配:根据任务负载动态调整CPU/内存(如YARN或Kubernetes调度器)。
存储优化:列式存储
-
Parquet/ORC格式:
- 按列存储,高压缩率(同列数据类型一致)。
- 支持谓词下推(Predicate Pushdown),仅读取需要的列。
-
数据分区分桶:按时间或哈希分区,加速过滤和聚合。
批处理系统架构
数据存储层
-
分布式文件系统
- HDFS(Hadoop Distributed File System) :分块存储(128MB/块),多副本冗余;适合存储大文件,如日志、原始数据。
- 云存储(S3、GCS) :对象存储服务,弹性扩展,按需付费。
-
数据湖(Data Lake)
- 原始数据存储:支持结构化(CSV)、半结构化(JSON)、非结构化(图片)数据。
- 列式存储格式:Parquet-高压缩率,适合OLAP查询;ORC-优化Hive查询性能,支持谓词下推。
-
数据仓库(Data Warehouse)
- 结构化存储:基于星型/雪花模型,优化聚合查询。
- 代表系统:Snowflake、Redshift(云原生数仓);Hive(基于HDFS的SQL查询引擎)。
计算引擎层
-
MapReduce(Hadoop)
- 经典批处理模型:分Map、Shuffle、Reduce三阶段。
- 适用场景:简单ETL、离线分析。
-
Apache Spark
- 内存计算:通过RDD(弹性分布式数据集)减少磁盘I/O。
- DAG调度:优化任务依赖,支持多阶段流水线执行。
- 统一API:支持批处理(Spark SQL)、流处理(Structured Streaming)、机器学习(MLlib)。
-
Apache Flink(批处理模式)
- 流批一体:同一API处理批和流数据(DataSet API)。
- 增量计算:优化迭代任务(如图计算、机器学习)。
-
Presto/Trino
- 分布式SQL引擎:直接查询数据湖(如Hive表、S3文件)。
- 联邦查询:跨数据源联合分析(如MySQL + Hive)。
资源管理层
-
Hadoop YARN
- 资源调度:将集群资源划分为容器(Container),分配给MapReduce、Spark等任务。
- 队列管理:支持多租户资源隔离(如生产队列 vs. 实验队列)。
-
Kubernetes
-
容器化部署:将批处理作业封装为Pod,动态扩缩容。
-
Operator模式:
Spark Operator:在K8s上原生运行Spark作业。
Flink Kubernetes Session:管理Flink集群生命周期。
-
工作流调度层
-
Apache Airflow
- DAG定义:通过Python代码定义任务依赖关系。
- 监控与重试:可视化任务状态,自动重试失败任务。
-
Oozie(Hadoop生态)
- XML配置:定义MapReduce、Hive、Sqoop任务流。
- 协调器(Coordinator) :定时触发工作流(如每天凌晨执行ETL)。
服务层
-
数据服务API
- RESTful API:通过HTTP接口暴露聚合结果(如统计报表)。
- GraphQL:灵活查询数据湖中的多源数据。
-
BI与可视化工具
- Tableau/Power BI:连接数据仓库,生成交互式仪表盘。
- Superset:开源BI工具,支持SQL Lab直接查询。
-
机器学习模型服务
- 批量预测:定期生成用户推荐列表、风险评分。
- 模型更新:每天训练新模型并发布到生产环境。
批处理应用场景
ETL(Extract, Transform, Load)
- 数据清洗:过滤无效记录(如空值、异常值)、标准化格式(日期、货币单位)。
- 数据转换:合并多源数据(如用户信息与订单记录关联)、计算衍生字段(如用户年龄=当前日期-出生日期)。
- 数据加载:将处理后的数据写入目标存储(如Hive表、Snowflake数仓)。
离线机器学习
- 特征工程:批量生成特征(如用户过去30天的购买总额)。
- 模型训练:在分布式集群上训练分类、回归或聚类模型。
- 离线评估:计算模型指标(如准确率、AUC)。
数据仓库构建
-
贴源层(ODS,Operational Data Store)
- 全量/增量同步:通过ETL工具从业务库(MySQL、Oracle)实时或定期抽取数据。
- 数据原样存储:保留原始格式(如JSON日志、数据库表结构),不做业务逻辑处理。
- 短期存储:通常保留最近7~30天的数据,供问题回溯使用。
-
明细层(DWD,Data Warehouse Detail)
- 数据清洗:处理缺失值、去重、统一格式(如时间戳标准化)。
- 维度退化:将多张表关联为宽表(如订单表+商品表→订单明细宽表)。
- 业务逻辑解耦:屏蔽底层业务系统的表结构差异(如不同系统的用户ID映射)。
-
汇总层(DWS,Data Warehouse Summary)
- 维度聚合:按时间、地域、产品等维度统计指标(如每日销售额、用户活跃数)。
- 主题域划分:按业务主题(如交易、流量、用户)组织数据。
- 中间结果复用:避免重复计算,加速上层应用查询。
-
应用层(ADS,Application Data Service)
- 业务指标封装:生成报表、BI看板所需的数据(如GMV、转化率)。
- 跨主题整合:融合多个主题数据(如用户画像+交易数据→高价值用户列表)。
- 数据服务化:通过API或数据接口暴露给业务系统(如推荐系统、风控系统)。
-
维度层(DIM,Dimension)
- 维度一致性:统一各层的维度定义(如地区、时间、产品分类)。
- 缓慢变化维(SCD)管理:处理维度属性变化(如用户地址变更)。
- 字典表存储:码值映射表(如状态码→中文描述)。
批处理系统优化
计算性能优化
内存计算 | 将中间数据缓存至内存,减少磁盘I/O。 | Spark RDD缓存、Flink托管内存 |
向量化执行 | 使用SIMD指令批量处理数据,提升CPU利用率。 | Apache Arrow、Presto向量化引擎 |
动态代码生成 | 运行时生成优化代码,避免解释执行开销。 | Spark Catalyst优化器、LLVM编译 |
数据本地性 | 调度任务到数据所在节点,减少网络传输。 | Hadoop机架感知、Kubernetes拓扑调度 |
数据倾斜处理
Salting(加盐) | 为Key添加随机前缀,将热点数据分散到多个分区。 | 大Key聚合(如用户ID热点) |
两阶段聚合 | 先局部聚合(Map端Combiner),再全局聚合(Reduce端)。 | 高基数Key统计(如PV计数) |
动态分区调整 | 根据数据分布自动调整分区策略(如范围分区→哈希分区)。 | Spark自适应查询(AQE) |
倾斜Key隔离 | 识别热点Key单独处理,其余正常计算。 | 电商大促期间头部商品订单分析 |
容错与一致性保障
检查点机制 | 定期保存任务状态,故障时从检查点恢复。 | Spark Checkpoint、Flink Savepoints |
幂等性设计 | 确保任务重试不会导致重复结果。 | 数据库UPSERT、Kafka生产者幂等配置 |
输出提交协议 | 仅在所有任务成功后提交最终结果,避免部分输出。 | Hadoop _SUCCESS文件、S3一致性模型 |
副本冗余 | 关键数据多副本存储,预防节点故障。 | HDFS 3副本策略、RAID存储 |
资源效率提升
动态资源分配 | 根据负载自动扩缩容,空闲时释放资源。 | Kubernetes HPA、YARN弹性调度 |
资源隔离 | 通过容器化(Docker)或队列隔离(YARN队列)避免作业间干扰。 | YARN Capacity Scheduler、Mesos资源隔离 |
流批资源复用 | 共享集群运行批处理和流任务,提高资源利用率。 | Flink统一运行时、Spark Structured Streaming |
存储与数据管理优化
列式存储 | 按列压缩存储,提升扫描效率。 | Parquet、ORC格式 |
数据分层存储 | 冷热数据分离,热数据存SSD,冷数据归档低成本存储 | HDFS分层存储、S3 Intelligent-Tiering |
增量处理 | 仅处理新增数据,避免全量重跑。 | Hudi/Iceberg增量更新、CDC(Change Data Capture) |
数据压缩 | 使用高效压缩算法减少存储与传输开销。 | Spark压缩配置、Kafka消息压缩 |
流批一体
Lambda 架构
- 批处理层(Batch Layer):处理全量历史数据(如Hadoop、Spark),生成精准结果。
- 速度层(Speed Layer):处理实时数据(如Flink、Kafka Streams),生成近似结果。
- 服务层(Serving Layer):合并批和流的结果(如HBase、Redis),供查询使用。
Kappa 架构
- 仅保留流处理层:通过流处理引擎(如Flink)重放历史数据,替代批处理层。
- 依赖持久化事件日志:如Kafka长期存储原始数据(支持全量回放)。
流批一体的核心思想
-
统一的数据模型
- 批数据:视为有界流(Bounded Stream),即流的一个有限子集。
- 流数据:视为无界流(Unbounded Stream),持续追加。
- 统一处理语义:无论数据来自历史还是实时,均通过 事件时间(Event Time) 和 处理时间(Processing Time) 统一管理。
-
统一的 API
- 声明式编程:通过同一套API(如SQL、DataStream/DataSet API)描述批和流任务。
- 代码复用:业务逻辑(如聚合、过滤)无需为批和流分别实现。
-
统一的运行时引擎
- 共享执行引擎:批和流任务在同一运行时中执行(如Flink的流式运行时支持批处理优化)。
- 资源动态分配:根据负载自动调配资源(如夜间跑批时分配更多资源)。
流批一体的技术实现
-
Apache Flink
-
流式优先,批是流的特例:
流处理:
DataStream API
处理无界数据,支持事件时间、状态管理、精确一次语义。批处理:
DataSet API
(旧版)或直接使用DataStream API
处理有界数据,自动优化执行计划。
- 批处理优化:对有界数据关闭冗余容错机制(如Checkpoint),减少开销。
- 动态延迟调度:批任务优先处理关键路径数据,缩短作业完成时间。
-
-
Apache Spark(Structured Streaming)
- 微批处理:将流数据切分为小批次(如1秒窗口),复用批处理引擎。
- 连续处理模式:实验性支持低至毫秒级的延迟(类似流处理)。