ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

离线数仓迁移到EMR Serverless Spark:资源利用率与成本优化的实践复盘

离线数仓迁移到EMR Serverless Spark:资源利用率与成本优化的实践复盘 做数仓的同学大多都有过这种体验白天业务高峰期集群上永远排着一长串作业前面跑不完后面就等着报表产出时间一拖再拖到了晚上和凌晨集群资源又大面积闲置CPU 利用率惨不忍睹可机器和机房的成本一分不少。这种“高峰不够用、低峰用不完”的资源矛盾在迅雷这类业务波峰波谷明显的公司里体现得尤其突出。今天想聊的就是迅雷把核心离线数仓任务逐步迁移到阿里云 EMR Serverless Spark 之后资源效率和业务交付速度的明显变化。这不是一篇简单的产品介绍而是把选型逻辑、迁移方法、参数调优、成本核算、踩坑记录都串起来的完整复盘。如果你正在自建 Hadoop 集群和 Serverless 方案之间纠结或者已经在用 Spark 但被资源管理折腾得够呛这篇文章应该能给你提供一条可以直接参考的路径。先交代一下背景。迅雷业务线多下载、影音、云盘等产品每天产生的日志量和业务数据量都不小数仓团队承接的是全公司的离线分析、报表产出、用户画像、推荐特征等任务。过去这些任务跑在自建 Hadoop 集群上YARN 调度队列按业务线划分高峰期大家抢资源低峰期资源空转系统的物理资源上限决定了并发上限想临时加资源根本来不及。后来我们把目光转向了阿里云 EMR Serverless Spark核心诉求就一个把资源和业务解耦让团队从“运维集群”回归到“加工数据”。1. 从自建集群的痛点说起1.1 资源的高峰与低谷远比想象中夸张先别急着聊 Serverless 多好得先把自建集群的真实处境说清楚。迅雷的数据任务有明显的“潮汐效应”每天上午 10 点到下午 6 点是业务查数和报表需求最密集的时段各种临时任务、看板刷新、数据订正任务全挤在一起晚上 10 点后进入离线批处理高峰ETL 任务一波接一波到了凌晨 2 点到早上 8 点大部分任务跑完集群基本处于半空闲状态。我见过不少团队的资源利用率数据自建集群能做到 40% 平均利用率已经算不错了。但问题的关键还不是平均利用率而是“高峰期的排队时长”。业务方不会关心你集群平均利用率是多少他们只关心上午 11 点提的数下午 3 点能不能出来。一旦集群资源被打满新提交的作业就只能进队列等着一等就是半小时甚至两小时。为了缓解排队我们做过很多努力调队列权重、限制大任务并发、把不重要的任务错峰调度但说到底集群的物理规模就摆在那里再怎么调度也是拆东墙补西墙。另一个隐性成本是运维人力。自建集群的版本升级、组件兼容、节点故障处理、磁盘满告警、NameNode 元数据维护这些事看着不大但每一件都会打断数据开发的正事。我们团队不算大运维占用的精力比例一旦超过两成留给数仓建模、数据治理、业务需求支持的时间就会被严重压缩。这也是后来推动迁移的一个重要原因——不是 Spark 跑不动了而是团队精力分配出了问题。1.2 为什么最终选择了 EMR Serverless Spark市面上能选的弹性计算方案不少有自建 Kubernetes 加 Spark Operator有普通 EMR 集群按需扩容也有各类 Serverless 产品。我们当时重点评估过三条路。第一条路是继续用自建集群但引入 Kubernetes 做弹性伸缩。思路是保留 YARN 作业不动用 K8s 动态拉起 NodeManager 来扩容。但实际评估下来这套方案的复杂度很高YARN 和 K8s 的联动要考虑节点注册、资源回收、镜像管理、网络打通、调度器配置相当于自己把云厂商的活儿干了一遍。而且自建集群的硬件资源还是自己先买好或预留好弹性只是“在已有资源池里腾挪”并没有真正解决资源天花板的问题。第二条路是使用阿里云 EMR 普通集群也就是经典的开源 Hadoop/Spark 集群形态按需创建、按量付费。这个方案的优点是环境熟悉迁移成本相对低但本质上还是要管理集群生命周期只是在“自己买服务器”和“云上开集群”之间换了个地方。高峰期仍然要考虑扩容时间节点拉起要等作业提交要等人还是得盯着集群状态。第三条路就是 EMR Serverless Spark。它把 Spark 作业的“运行环境”完全托管了我们只提交作业不用关心 Executor 跑在哪台机器上、集群有多少节点、要不要扩容。作业来了平台自动分配资源作业跑完资源自动释放。这个模式直击了“高峰不够用、低峰用不完”的痛点——资源不再是预先购买、固定存在的物理实体而是按作业需要动态出现的计算能力。最终选择它还有一个很现实的原因阿里云 EMR Serverless Spark 兼容开源 Spark API 和生态我们团队现有的 Spark 作业、SQL 脚本、开发习惯基本可以平移不需要为了用 Serverless 而重写一套代码。对于一个已经有大量存量任务的数据团队来说这一点比任何 fancy 的特性都重要。2. 迁移前的架构梳理与作业盘点2.1 迁移前的数仓架构长什么样在动手迁移之前我们先把现有数仓的底账摸了一遍。迅雷的离线数仓是典型的四层结构ODS 层存原始日志和业务库同步数据DWD 层做清洗和明细加工DWS 层按主题汇总ADS 层面向具体应用出报表和指标。调度上用的是 DolphinScheduler任务之间靠依赖关系编排核心任务有数据质量校验跑挂了会自动告警重跑。数据存储这一层当时还在 HDFS 上Hive 表有几千张其中热表、常用表集中在几十张核心表上。计算引擎以 Spark 为主少量任务还在用 Hive MR 跑。整个架构从设计上讲没什么大问题真正的瓶颈在计算资源层Spark Executor 的调度受制于 YARN 队列配置任务与任务之间相互挤占核心任务和非核心任务难以做到真正的资源隔离。这里要特别强调一下迁移计算引擎不等于迁移存储。我们的做法是先把计算层迁到 EMR Serverless Spark存储层暂时不动仍然读写原 HDFS 或 OSS 上的数据文件。这样做的好处是风险可控就算计算层出了问题数据还在原地随时可以切回原有计算方式。等到计算层跑稳定了再考虑把部分存储迁移到 OSS 或阿里云数据湖构建服务。2.2 作业分级哪些先迁哪些后迁几千个作业不可能一次性全迁过去这是迁移项目最容易犯的错误。我们按“重要性、稳定性、资源消耗”三个维度把作业分成了三类。第一类是核心报表和线上数据依赖的任务特点是每天必须准时产出出问题影响面大。这类任务我们放在最后迁移先用双跑的方式验证数据一致性确认无误后再切换。第二类是常规 ETL 任务特点是调度稳定、逻辑相对固定这类任务适合作为迁移的主力分批推进。第三类是临时查询和探索性分析任务特点是可以容忍排队和失败重跑这类任务是我们最早迁到 Serverless 的一批因为即使出现问题影响面也最小。实际执行的时候我们定了一个“先小后大、先跑通再优化”的节奏。第一批选了十几个作业涵盖 Spark SQL、PySpark、Spark Scala 三种作业类型先把提交方式、参数配置、日志查看、告警对接这些基础链路跑通。第二批扩大到几百个作业覆盖主要业务线的常规 ETL。最后一批才是核心报表任务逐个双跑验证数据量、产出时间、结果明细都对比通过之后再切换。2.3 调度系统和 Spark 作业怎么衔接把作业迁到 Serverless Spark 之后调度层不能乱。我们继续沿用 DolphinScheduler 作为总调度只是把“提交到 YARN 集群”的环节替换成了“调用 EMR Serverless Spark 的 OpenAPI 提交作业”。这里有一个设计上的关键点调度平台只负责依赖编排和触发时机不负责管理计算资源。每个被调度触发的作业通过脚本调用 Serverless Spark 的提交接口传入作业名称、SQL 文件或 Python 脚本地址、资源配置参数、依赖的 Jar 包位置等信息。作业在 Serverless 环境中运行调度平台通过轮询接口获取作业状态成功或失败都由调度平台统一记录和告警。这个方案的好处是业务方和调度系统几乎无感知迁移前后看到的还是一个“按依赖关系运行的任务流”只是底层执行引擎变了。对数据开发团队来说学习成本也被压到了最低平时怎么写 Spark 作业现在还是怎么写只是多了一层提交封装。3. EMR Serverless Spark 的核心配置与调优3.1 资源规格Driver 和 Executor 的配比怎么定我们刚开始用 EMR Serverless Spark 的时候分配资源比较粗放就是照着原先 YARN 作业的 Executor 数量直接换算。但用了一段时间后发现Serverless 场景下资源是按作业维度申请和计费的同样的资源配置运行效果可能和 YARN 下差别很大因为集群不再有“常驻资源池”的缓冲每个作业的资源规划得更细致。一个典型的 Spark SQL 作业我们一般这样配Driver 用 2 vCPU、4 GB 内存Executor 用 2 vCPU、8 GB 内存每个 Executor 挂 2 到 4 个 core内存给 8 GB 到 16 GB。具体的配比取决于作业本身的 shuffle 量和数据规模。数据量小、逻辑简单的作业不需要配太多 Executor避免资源浪费数据量大、join 和 group by 多的作业要重点考虑 Executor 数量和 shuffle 分区数之间的匹配关系。这里给一个比较实用的建议看 Spark UI 的 Executor 运行记录如果大多数 Executor 的 GC 时间占比偏高说明内存不够或者单个 Executor 处理的数据量太大如果 Executor 的 Shuffle Read 量分布非常不均匀那大概率是数据倾斜而不是资源不够。这两种情况对应的解决方式完全不同前者加内存后者要处理倾斜后面会详细讲。3.2 Spark 作业参数的几个关键调整迁移过程中我们重点调过几个 Spark 参数这些参数在自建集群上可能没那么敏感但在 Serverless 模式下直接关系到成本和稳定性。第一个是动态资源分配。开启动态分配后Spark 会根据作业的 stage 执行情况动态调整 Executor 数量空闲时自动释放繁忙时自动追加。我们线上配置了spark.dynamicAllocation.enabledtrue、spark.dynamicAllocation.minExecutors1、spark.dynamicAllocation.maxExecutors20这类参数。动态分配对批量小任务特别友好不会出现小作业也占用一大堆 Executor 的情况。第二个是 shuffle 分区数。spark.sql.shuffle.partitions默认值是 200但在 Serverless 模式下如果数据量不大200 个分区反而会产生大量小 task调度开销远大于计算收益。我们一般根据数据量手动设置比如单表扫描量在几十 GB 级别的作业分区数设到 100 左右就够了几百 GB 的作业再适当调高到 300 到 500。经验公式不复杂目标分区大小控制在 200 MB 到 500 MB 之间比较合理分区数 总 shuffle 数据量 / 目标分区大小。第三个是任务重试。Serverless 平台上偶尔会遇到节点故障导致任务失败合理的重试机制是必要的。我们配置了spark.task.maxFailures4允许 task 在遇到节点级故障时自动在其他计算资源上重试而不是整个作业直接失败。这个参数看着不起眼但在长任务上能省掉大量人工介入的时间。3.3 成本控制按量付费和预留资源的组合策略说到 Serverless大家最关心的往往不是性能而是钱。EMR Serverless Spark 的成本模型很简单按实际消耗的 vCPU 和内存计费作业跑多少算多少。这种计费模式对低峰期很友好但高峰期如果大量固定作业同时跑费用也会随之上涨。我们的应对策略是“按量 预留”混用。对于每天晚上固定跑批的核心作业我们评估了一下规律性这些作业每天产生的资源消耗相对稳定按照历史数据算出一个合理的预留资源量在高峰期预留一部分资源池。对于临时查询、新任务验证、波动性较大的作业走按量付费跑完就释放不为不确定的需求买单。这里有一个成本对比的思路值得参考不要把 Serverless 的费用和“原来集群的总成本”直接比而要对比“有效计算成本”。原先自建集群虽然总成本固定但其中有大量资源是闲置的有效利用率可能只有 40% 到 50%。Serverless 模式下花费的钱基本都对应实际计算量性价比的差异要看这个维度。我们迁移后的实际感受是在同等业务量的前提下计算资源总成本大约下降了 20% 到 30%但这不是最关键的收益——最关键的是排队时间几乎消失了。4. 资源效率提升的量化对比与分析4.1 先定义指标否则“效率提升”只是空话迁都迁了总要回答“到底提升了多少”这个问题。很多团队做完迁移后只会说“感觉快了很多”这是不够的。我们把资源效率和业务提升拆成了几个可量化的指标。核心指标包括作业排队等待时长即作业提交到开始执行的时间间隔作业平均执行时长即 Spark 作业从开始到结束的运行时间集群资源平均利用率迁移后虽然没有固定集群了但可以通过各作业的资源申请量和使用量的比值来衡量单位作业计算成本把每个作业产生的费用汇总后除以作业数量或数据处理量报表产出及时率即关键报表在 SLA 时间内完成产出的比例。这些指标在迁移前就要开始采集和记录。我们花了两周时间把存量作业的历史运行数据整理出来包括调度平台上的开始时间、结束时间、运行日志中的耗时记录再结合集群监控系统里的资源使用曲线形成一份迁移前基线。没有这个基线后面的对比分析就无从谈起这一点希望所有做迁移项目的同学务必重视。4.2 迁移前后核心指标对比在业务量基本持平的前提下我们对比了迁移前后一个月的数据这里说说几个比较有代表性的变化。排队等待时长方面迁移前高峰期作业平均排队时间在 15 到 40 分钟之间遇到大任务抢占资源时个别作业排队超过 1 小时也不罕见。迁移后由于资源是动态申请的作业提交后基本是秒级启动排队现象几乎消失平均排队时间降到了 1 分钟内。作业平均执行时长方面迁移前单个 ETL 作业的平均执行时长大约 22 分钟迁移后降到 15 分钟左右。缩短的原因一方面是资源不再被其他任务挤占另一方面是我们在迁移过程中顺手做了一批参数调优比如分区数合理化、小文件合并这些优化在自建集群上也能做但在 Serverless 模式下收益更明显。资源利用率方面如果只算有效计算资源消耗自建集群的 CPU 平均利用率在 40% 上下迁移后按作业实际申请资源与执行计算资源消耗的比值来算利用率能稳定在 70% 以上。这说明同样一单位资源真正花在计算上的比例大幅提高。这些数字只是我们项目中的观察结果不同团队的数据会不一样。但方向上我可以比较确定地说资源效率的提升主要来自“资源不再被闲置和排队浪费”而不是 Spark 程序本身变快了多少。4.3 业务效率提升的间接收益资源效率提升带来的最直接业务变化是报表产出时间提前。原先每天早上 8 点前必须产出的核心经营报表迁移后普遍能提前 1 到 2 小时完成。这一点对业务方感知特别强因为他们是按“几点能看到数据”来评判数据团队工作的不会关心你底层用了什么技术。另一个间接收益是需求响应速度变快了。以前业务方提一个数据需求开发同学要先评估集群资源够不够碰巧赶上高峰期可能就只能排队等需求排期动不动拖到下周。现在资源按需申请开发验证环境随时能跑从提需求到上线周期从一两周压缩到三四天是常见的。老板看到这个变化数据团队在公司里的存在感都会不一样。此外运维负担减轻后团队开始有余力做过去一直想做但没时间做的事比如数仓模型的规范化治理、指标口径的统一、历史数据归档。这些工作短期看不到直接的业务收益但时间久了会发现查询效率、开发效率、数据质量都在提升属于典型的“磨刀不误砍柴工”。5. 迁移过程中的踩坑与排查实录5.1 冷启动和长尾任务的那些坑Serverless 不是完美的第一个要面对的就是冷启动问题。作业提交后平台需要拉起 Driver、分配 Executor、加载依赖环境这个过程需要一些时间。对于大部分批处理作业几十秒的启动时间可以接受但对那些耗时本身只有一两分钟的小任务冷启动时间占比就非常明显用户的感知是“我怎么感觉还没以前快”。我们当时的处理办法是尽量合并小任务。原先在自建集群上开发习惯是一个业务逻辑写一个作业导致大量 1 到 3 分钟的小作业。迁移后我们把同一数据源、相同调度频率的小作业合并成一个作业通过多段 SQL 或 PySpark 脚本在内部串联。作业数量从几千个降到了几百个不仅减少了冷启动的影响还让调度链路更清爽、更容易排查问题。长任务方面我们也遇到过个别 Executor 因节点故障被回收导致 stage 反复重算的问题。排查后确认主要是数据倾斜引发的个别 task 长时间运行超过了平台对单 task 的时长限制。解决思路是先把倾斜问题处理掉而不是盲目调大超时时间具体做法后面会讲。5.2 数据倾斜最容易被误判为“资源不够”的问题数据倾斜在自建集群上就会出现但迁移到 Serverless 后更容易暴露因为每个作业的资源是动态分配的一旦某个 task 数据量过大其他 Executor 都在空等作业整体跑得特别慢还容易被误判成资源不足而盲目扩容。我们线上遇到过一个典型案例一张用户行为明细表按用户 ID 分组统计少数头部用户的行为数据量是普通用户的几百倍导致这几个 key 所在的 task 跑了几个小时其他 task 十几分钟就结束了。后来在 SQL 里加了随机前缀做两阶段聚合先把数据打散再对热点 key 单独处理执行时间从几个小时降到了二十分钟以内。如果你不确定一个作业是不是数据倾斜一个快速判断方法是用 Spark UI 看各个 task 的 Shuffle Read 和运行时长如果某个 task 的 Shuffle Read 是其他 task 的几倍甚至几十倍基本可以确定是倾斜。解决方案根据场景有多种比如加盐两阶段聚合、广播小表、热点 key 单独 join具体选哪种要看业务逻辑。这个排查思路在 Serverless 和自建集群上完全通用是每个 Spark 开发人员都应该掌握的基本功。5.3 网络打通与元数据服务的衔接细节最后一个容易忽略的坑是网络和元数据配置。EMR Serverless Spark 的计算资源在云平台上要读写我们原有数据存储就必须先把网络打通。我们当时使用阿里云 VPC 环境数据源和 Serverless 资源需要在同一个 VPC 或者通过安全组规则放通访问。这里有个容易踩的细节Hive 元数据服务如果还部署在自建集群上Serverless 的 Spark 作业要访问它同样要打通网络并且注意访问权限和并发限制。我们一开始就是元数据服务连通性没处理好导致作业频繁报 “Table not found” 或 “Connection refused”排查了半天才发现是安全组没有放行对应端口。后来我们把常用表的元数据逐步迁移到了阿里云的数据湖构建服务DLF对接过程中把 Hive Metastore 的地址、数据库名、表名做了清晰映射作业访问的稳定性明显提升。如果你也打算迁到 Serverless 平台建议提前梳理清楚元数据服务的连接方式不要等到业务切换时才发现网络不通那种临门一脚发现门锁了的感觉真的非常酸爽。另外存储路径的兼容性也值得注意。我们原始数据在 HDFS 上迁移过程中逐步把部分热表的数据文件复制到 OSS因为 HDFS 和 OSS 的访问协议、文件数量限制、命名空间管理都不太一样。OSS 上的文件如果小文件特别多也要做合并压缩处理否则会产生大量的 list 操作和连接开销直接影响 Spark 作业的读取效率。我们后来统一用 Parquet 格式加适当压缩存储在数据读取速度和存储成本之间找到了一个平衡点。整体上这次迁移带给我们的收获除了资源效率提升和成本下降更重要的是让团队重新把精力放到了“数据”本身。以前有一半脑子要想着集群别出问题现在只需要想清楚怎么加工数据、怎么提升模型质量。数仓的价值是靠数据质量和业务洞察体现的不是靠能运维多少台机器体现的——想明白这一点技术选型的方向其实就清晰了。
返回列表