ARTICLE DETAIL

资讯详情

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

云上存算分离架构实践:从Hadoop迁移到对象存储的成本与性能优化

云上存算分离架构实践:从Hadoop迁移到对象存储的成本与性能优化 1. 为什么存算分离在云上成了必答题——先从资源利用率的账算起过去几年我一直在帮企业搭数据平台早期最常用的套路就是自建Hadoop集群HDFS存数据YARN跑计算。这套方案在物理机房时代没什么大问题但搬到云上之后第一个让我意识到“必须改”的场景是某次帮客户看集群账单。那是一个12节点的CDH集群配置不算低SSD加机械盘混布每季度都要扩容。我打开监控面板一看CPU平均利用率不到25%但存储已经从60%涨到接近90%。最尴尬的是业务方不断提需求说“数据要保留更久”“历史数据不能删”但存储扩容意味着CPU和内存也跟着扩——因为HDFS的DataNode和计算节点是绑在一起的。你只想买“硬盘空间”却被迫连CPU、内存、机架带宽一起买单。这就是存算耦合最典型的资源浪费存储需求拉动计算资源空转。云环境把这个问题放大了。云上计费是按资源种类分开算的对象存储每GB每月几毛钱而计算节点按小时计费、按规格计费。如果还是沿用HDFS加物理机的思路等于主动放弃了云平台最大的红利——按需伸缩。到了任务低谷期集群空转账单照扣到了高峰期集群扩容又受限于数据本地性必须等数据平衡。所以越来越多的团队开始认真考虑一件事把数据放在独立存储层计算集群按需拉起用完就释放。这就是存算分离最朴素的出发点。从技术演进的时间线看存算分离其实经历了几个阶段。最早是计算与存储进程分离比如HDFS Federation、NameNode与DataNode分离管理然后是物理部署分离比如计算节点不挂大数据盘数据统一走网络访问远端的存储集群到了云原生阶段就是彻底把存储抽象成对象存储服务计算引擎通过S3协议或各云厂商的对象存储接口访问数据。现在的“存算分离最佳实践”基本默认指最后这种形态对象存储作为数据底座Spark、Hive、Presto/Trino这些计算引擎无状态化地跑在临时集群上。这里要澄清一个常见误解存算分离不是说把HDFS删掉换成对象存储就完事了。它是一整套架构模式的变化涉及元数据管理、表格式选型、数据读写路径、缓存设计、权限模型、成本核算方式等等。很多人一上来就把Hive表路径指到OSS或者S3跑几个查询发现“还行”但没有意识到自己只是把HDFS换了个名字真正的架构红利一点没吃到。后面我会逐步拆开讲。2. 云环境下存算分离的核心架构拆解计算层、存储层与元数据层2.1 计算层无状态化是一切的前提存算分离之后计算集群最重要的特征就是无状态。什么叫无状态就是任何一个计算节点挂了或者整个集群被销毁了都不影响数据本身也不影响后续任务重新拉起。传统Hadoop里DataNode既存数据又跑任务节点挂了数据会丢副本任务重试要等数据恢复。而存算分离下计算节点全部是无状态的Worker它们只负责读远端存储上的数据并执行计算逻辑本地磁盘顶多放一些临时shuffle数据和缓存任务一结束这些临时数据就可以舍弃。实际操作层面无状态意味着两件事。第一集群可以“用完即弃”。比如每天晚上跑一次大规模离线ETL那就每天定时拉起一个Spark集群跑完释放。云厂商的EMR、Databricks、阿里云E-MapReduce这类服务都支持这种模式最快几分钟拉起一个几十节点的集群任务完事之后缩容到零。对于有固定SLA的生产任务可以保留一个常驻小集群处理低延迟需求大任务用弹性集群扛高峰。第二扩缩容不需要再等数据重新平衡。传统HDFS集群扩容后要等DataNode数据平衡完成才能充分发挥性能存算分离下新节点拉起就能直接从对象存储读数据几乎零等待。计算引擎的选择上目前主流就是Spark、Hive/Tez、Presto/Trino这几类。Spark适合复杂ETL、机器学习特征工程Hive适合纯SQL批处理如果团队已经有很多HiveQL脚本迁移成本最低Presto/Trino适合交互式查询。这些引擎在存算分离架构下都做了对象存储适配比如Spark的S3A connector、Hive的AWS S3集成、Trino的Hive connector直读对象存储。我觉得选型时不用太纠结核心看团队的技术栈和任务类型。2.2 存储层为什么对象存储成了默认选择存算分离的存储层绝大部分团队最终都会倒向对象存储。原因很实在便宜、容量无限、支持按量付费、自带多副本冗余。相比之下自建HDFS需要考虑三副本、坏盘替换、集群扩容运维成本高云上的块存储EVS/ESSD虽然性能好但价格比对象存储贵一个量级存海量历史数据不划算。对象存储有哪几个核心特性是存算分离必须依赖的无限容量不用担心扩容和分区上限数据从TB级到PB级都能平滑承载。数据持久性云厂商一般承诺11个9或更高的持久性通过多可用区冗余实现相当于底层帮你做了多副本。独立计费只存数据就只付存储费不绑定任何计算资源。S3协议兼容几乎所有大数据引擎都原生支持S3协议跨云迁移的学习成本低。但对象存储也有天然短板最典型的是小文件性能差和延迟比本地磁盘高。对象存储的架构决定了它适合“大文件流式读写”对“大量小文件随机访问”非常不友好。这个我在第3节会专门展开因为它是从HDFS迁到对象存储后第一个暴雷的点。另外存储层的目录组织也很重要。很多人直接把Hive表的数据路径挂在对象存储的某个桶下面但完全不规划桶的层级。我建议桶结构这样设计bucket-name/ ├── warehouse/ # Hive数仓表数据 │ ├── dwd/ │ ├── dws/ │ └── ads/ ├── logs/ # 原始日志 ├── tmp/ # 临时数据、任务中间结果 ├── backup/ # 冷备归档 └── exchange/ # 与外部系统交换数据这样设计的好处是成本管理清晰可以按前缀设置生命周期规则比如logs目录30天转低频、tmp目录7天清理权限管控也更细粒度。2.3 元数据层表格式与Metastore的重新定位存储层换成对象存储之后元数据管理变得比以前更重要。因为HDFS本身有目录和文件的概念NameNode负载了元数据但对象存储没有“目录树”这种概念它天然是KV结构。那么Hive表的分区、文件列表、Schema等元信息必须由独立的元数据服务来管。这就是Hive MetastoreHMS的职责。于是问题来了Hive表底层数据放在对象存储上HMS是不是还够用答案是够用但光靠HMS不够。HMS解决的是“表结构和分区映射到哪个路径”的问题比如查dwd_order_detail表HMS告诉引擎这个表有哪几个分区、每个分区对应的对象存储路径前缀是什么。但HMS本身不知道每个分区下有哪些文件、每个文件有多大这个信息是引擎在查询时动态list目录拿到的。传统HDFS下NameNode能快速返回目录列表对象存储不行list操作对大量小文件是灾难级的慢。所以存算分离架构里表格式Table Format的选择变得至关重要。目前主流有三种Apache Hudi、Apache Iceberg、Delta Lake。它们都提供了一层“文件级别的元数据管理”把自己的元数据也存到对象存储上查询引擎通过读这些元数据直接定位需要读取的数据文件而不用全量list目录。用Iceberg举个具体例子一张表的每个快照对应一个Manifest清单Manifest里记录了所有数据文件的路径、列统计、行数等信息。Spark查询时先读Manifest根据过滤条件直接跳过不需要的文件然后再去对象存储拉取目标文件。这就把“list海量文件”的操作变成了“读几个小清单”性能提升非常明显。如果你在用Hive表直接挂在对象存储上数据文件从几千个涨到几十万个之后查询慢到你想砸键盘解决方案十有八九是迁移到这类表格式。表格式选型没有绝对标准我个人的参考维度是维度HudiIcebergDelta Lake流批一体支持得很好有增量拉取能力偏批处理流场景需要额外组件偏批处理Databricks生态强并发写支持乐观锁并发支持乐观锁并发支持但多写冲突处理一般小文件自动治理有Clustering有Compaction有Optimize查询引擎支持Spark/Flink/Hive/TrinoSpark/Flink/Trino/StarRocksSparkDelta Lake原生其他需额外配置社区活跃度成熟度高阿里云、各类云厂商支持多Databricks主导如果你做的是以Spark SQL为主的离线数仓Iceberg上手最平滑如果你有大量Flink实时写入场景Hudi更顺手。当然这不是什么金科玉律具体还要看贵司基础设施的云厂商是谁很多云厂商的EMR已经把某个表格式做了一键集成选择生态支持最好的那个就行。2.4 权限与认证从文件系统权限到云服务授权存算分离还牵涉一个经常被忽略的环节权限模型。HDFS时代权限一般是Linux文件权限加ACL数据存储在集群内部网络边界相对清晰。但对象存储是开放服务访问它需要云账号的AK/SK或临时凭证。权限设计不好要么出现安全漏洞要么出现“任务跑不起来”。我的推荐做法是三层权限设计云账号层用子账号或RAM角色只授最小权限。比如计算集群绑定的服务角色只允许访问指定的bucket前缀。引擎层Spark/Hive作业通过使用角色的临时凭证访问对象存储不使用硬编码的AK/SK。如果团队已经有Ranger或者类似的开源权限组件可以把它接入HMS控制的是SQL级表权限。存储层对象存储的Bucket Policy做最后的兜底禁止公共读写。很多团队嫌麻烦直接在作业代码里写死一个AK/SK。如果你只是内网实验问题不大但在生产环境一旦AK/SK泄露对象存储里的所有数据都裸奔。而且等被安全团队巡检到整改时的迁移量远超你省下的那点功夫。从第一天就做好STS临时凭证的接入是存算分离最佳实践里性价比最高的一步。3. 计算与存储解耦后的三大关键技术问题小文件、本地性与缓存3.1 小文件问题为什么对象存储“怕”小文件以及怎么治理小文件问题在HDFS时代就存在但存算分离把它放大了。先说原因。HDFS的NameNode把每个文件、每个目录都作为内存中的一个对象来管理大量小文件会压垮NameNode这个大家比较熟悉。对象存储没有NameNode但也有自己的脾气它的每个请求都有独立的开销处理大文件时按顺序分段读效率很高但如果是成千上万个小文件引擎要不停地发HTTP请求每个请求的延迟可能在几十毫秒到几百毫秒加在一起就是灾难。比如一张表有10万个小文件Spark做一次全表扫描就要发10万次请求光网络握手时间就让人崩溃。最典型的是实时写入场景。Flink或Spark Streaming实时写Hive/Hudi表时如果Checkpoint间隔很短每个Checkpoint都会产生一批小文件比如5分钟一个Checkpoint一天下来就有288个分区目录每个分区里又有很多几MB的小文件日积月累查询性能直线下降。治理方案我给了两类。第一类是在写入侧控制文件大小。Spark写数据时用repartition或coalesce控制输出文件数目标文件大小尽量在256MB到512MB。比如一张表一天的数据量是200GB如果设置每个文件256MB输出文件就是800个左右。这个量级对对象存储比较友好。Flink实时写入Hudi时可以把hoodie.parquet.small.file.limit参数设大一些让Hudi自动把小的写入合并到已有文件组里。第二类是定期做小文件合并。Iceberg/Hudi都提供了compaction或clustering能力建议在每天的低峰期跑一次合并任务把小于某个阈值如128MB的文件合并成256MB以上的大文件。我们项目的经验是每天凌晨1点跑一次合并白天的交互式查询性能能稳定保持在秒级如果连续一周不治理到了周四下午准时出现查询超时告警。3.2 数据本地性失效拿什么补偿网络IO存算分离一个很大的心理落差是以前Spark跑在HDFS上有数据本地性调度Task尽量调度到数据所在的节点上读数据基本走本地磁盘现在数据在远端对象存储上每个Task读取数据都要走网络延迟变高了。这是不是意味着存算分离性能一定比HDFS差不一定但需要做三件事来补偿。第一列式存储加谓词下推。对象存储上建表尽量用Parquet/ORC格式Spark或Trino读取时通过Parquet的row group统计信息做谓词下推只读取必要的列和行。这比“全量拉数据再过滤”省太多网络流量了。建表时把谓词下推相关参数打开比如Spark的spark.sql.parquet.filterPushdowntrue和spark.sql.parquet.aggregatePushdown。第二并行读与读放大控制。引擎从对象存储读大文件时一般会按一定大小拆分成多个Range并行读。比如Spark读取一个512MB的Parquet文件可能拆成16个32MB的块并发拉取。这样能充分利用带宽。要注意的是块大小设置不能太小比如小于8MB否则请求数暴增反而更慢。第三网络与带宽规划。存算分离集群的节点建议和对象存储放在同一个Region最好同一个可用区。跨地域访问对象存储延迟和带宽成本都不可控。如果使用云厂商的EMR服务控制台一般会直接默认同Region这个基本不用额外操心。但如果你用的是自建的K8s集群一定要注意把Pod调度到和对象存储同Region的节点不要等到数据都跑起来再发现跨Region流量账单。3.3 缓存层Alluxio与轻量本地缓存既然远端读取慢能不能把热点数据缓存在计算节点的本地磁盘能这就是缓存层的角色。最常被提起的组件是Alluxio。它提供的是一个分布式缓存层介于计算引擎和底层存储之间把经常读的数据文件缓存到集群本地或者内存中下次读取直接命中缓存。它在HDFS时代就被用于加速数据访问到了对象存储时代价值更明显因为它把远端对象存储“伪装”成了一个类似HDFS的文件系统引擎读它的时候如果命中了缓存能省掉几乎全部的网络IO。但Alluxio带来的问题是引入了一个新的分布式系统它的元数据服务也需要部署和维护复杂度并不低。对中小团队来说我先建议从轻量方案开始利用计算引擎自带的缓存能力。Spark 3.x 的DataSource V2支持自定义数据源配合SSD本地盘可以启用缓存。Trino的Hive Connector支持hive.cache.enabledtrue和hive.cache.ttl等配置把最近读过的文件缓存在本地。Presto同样有类似配置。这些轻量缓存的逻辑很简单读一次远程文件后在本地留一份副本短时间内再次访问直接读本地。它们没有Alluxio那么智能、没有统一管理面但对绝大多数场景已经够用了尤其是数据报表、交互式分析的固定查询模式——每天早上业务方都会看同一组核心指标这些指标的底层数据文件其实就那几个缓存命中率非常高。我的经验是先跑业务等观察到“某些查询反复读同一批大文件”、“每天同一个查询的调度次数很多”再考虑引入专门的缓存层。一上来就上Alluxio反而是给自己找运维负担。关于这个小结论我和不少数据平台团队的同行聊过大家体感基本一致。4. 从传统Hadoop平滑迁移存量数据迁移与双跑验证的实操路径4.1 迁移前的存量摸底目录清单与数据量级存算分离改造本身不复杂复杂的是存量系统的平滑迁移。我见过太多团队急着把Hive表路径切到对象存储结果存量数据没迁完增量也在写两边数据对不上最后回滚成一团粥。所以第一步一定是先摸清现状。你需要做一张存量清单至少包含这些字段库名、表名表格式内部表还是外部表存储格式是Parquet还是ORC是否分区表各分区数据量、文件数量、平均文件大小表的最近访问时间确定哪些是活表、哪些是死表表的写入方式离线调度写、实时流写、手动Insert这张表怎么来可以写一个脚本扫描HMS元数据也可以直接用SHOW TABLE EXTENDED逐个看数据量大的话建议写个Python脚本连上HMS的内置数据库直接查。存量摸底的意义在于你不可能一夜之间把所有表都迁过去必须挑选合适的迁移批次。4.2 全量迁移与增量同步DistCp的进阶玩法全量历史数据迁移最通用的工具是Hadoop自带的DistCp。传统上它用于HDFS集群间的数据复制但Hadoop 3.x版本以及云厂商适配的版本都支持把对象存储作为目标端。基本命令是hadoop distcp \ -Dfs.s3a.access.key${ACCESS_KEY} \ -Dfs.s3a.secret.key${SECRET_KEY} \ -Dfs.s3a.endpoint${OSS_ENDPOINT} \ -Dfs.s3a.path.style.accesstrue \ hdfs://namenode:8020/user/hive/warehouse/dwd_order_detail \ s3a://bucket/warehouse/dwd_order_detail这里有几个细节要注意如果目标Bucket已经存在同名文件默认会覆盖建议先用-update参数做增量覆盖。大文件复制建议开启s3a.fast.upload分段并行上传速度更快。迁移期间要保证源端没有被“强一致写入”否则中间态数据复制过去是不一致的。同一个目录建议起多个DistCp任务并行跑用-numMaps参数控制并行度。但也要小心如果把对象存储的写入限额打爆所有任务一起失败所以并行度要循序渐进地调。全量迁移结束后要做一个校验。DistCp自带-verify参数可以逐文件比对长度和校验和但这在对象存储上比较耗时。一个更实用的做法是全量迁移完成后停掉写入任务只读不写让增量任务追平再做一次关键分区的大小和行数校验确认一致后再切换读流量。增量同步这一块要看你有多少流式写入任务。如果只是离线数仓每天定时写一次增量分区那很简单全量迁移后第二天正常调度跑增量任务写的目标路径直接切到新的对象存储路径。如果有Flink实时写Hive/Hudi的任务建议在切换前先把Flink作业停止等全量迁移完成Flink作业换新路径和新的Checkpoint状态再启动。千万不要尝试“新数据写新路径、老数据写老路径”同时并行让引擎自己解决——大部分实时管道都不支持这种透明合并会丢数据。4.3 计算引擎适配三行配置让Spark/Hive读上对象存储数据就位后计算引擎的适配其实就是一套core-site.xml和Hive/Spark的配置改动。以Spark为例核心是配置S3A文件系统实现configuration property namefs.s3a.impl/name valueorg.apache.hadoop.fs.s3a.S3AFileSystem/value /property property namefs.s3a.endpoint/name valueoss-cn-hangzhou.aliyuncs.com/value /property property namefs.s3a.path.style.access/name valuetrue/value /property property namefs.s3a.fast.upload/name valuetrue/value /property /configuration然后Spark作业里的输入输出路径从hdfs://...换成s3a://...即可。Hive的适配更简单建外部表的时候直接指定LOCATION s3a://bucket/warehouse/xxx。但光改路径还不够我强烈建议在切换前把一个典型的ETL任务在新路径上走一遍“影子跑”看看有没有三类问题路径兼容性问题比如Hive的UDF里写死了hdfs://前缀或者某些配置项不支持S3A。写并发问题对象存储对高频写请求有限制如果某个任务的输出文件数量特别大容易触发限流需要调高并发数或者合并输出。延迟问题首轮读对象存储的数据文件可能需要缓存热点第一次跑会偏慢第二次跑才能回到正常水平。别因为这个误判性能。4.4 双跑验证与灰度切换不搞“一夜转完”迁移最忌讳的就是“大爆炸式切换”。我推荐的节奏是选一个核心报表表先迁移它做七天双跑新老两套任务跑同样的逻辑对比产出结果。如果每天结果完全一致再切流量。切流量时先切只读任务报表、即席查询观察一天确认无问题再切写任务。最后再批量迁移剩余表。这一批可以按热度排优先级冷表直接一次性迁完不用做双跑。双跑期间会有两倍计算资源的开销但相比一次迁移事故导致的半夜回滚这点成本非常划算。等到核心任务全部切过来再把老集群降级或释放计算资源账单立刻大幅下降。5. 成本账单里的真相存算分离省在哪里、费在哪里5.1 存储成本对比三副本与分层存储的差距传统HDFS默认三副本意味着你存1TB的逻辑数据实际占用3TB物理空间。加上HDFS的DataNode节点通常配比是“CPU内存大盘”存储成本不单单是硬盘价格还要摊上节点本身的CPU和内存。而对象存储底层虽然也做冗余但对用户来说按实际逻辑大小计费不用理解副本数。常驻Cluster按年付费如果是低频访问还能再往低级存储层迁移。我拿一个真实案例算一笔账。一个客户大约存储了800TB逻辑数据。之前用自建HDFS三副本加节点开销每月存储相关成本在18万元左右。迁移到对象存储后热数据存标准型90天未访问转低频180天未访问转归档实际账单降到每月约6万元存储成本直接砍掉三分之二。当然不同云的定价策略不同但趋势是一致的。所以存算分离省下的第一笔钱来自存储层。这笔钱不是省出来的是“算”出来的——你得主动设计方案让数据在不同的温度层级间流转。5.2 计算成本弹性集群与竞价实例的组合拳计算侧存算分离的最大省钱点是弹性。离线任务在夜间集中运行就可以配置定时扩缩容晚上8点扩到100个节点凌晨2点缩回10个节点。传统HDFS集群做不到因为缩容会导致数据块副本数下降HDFS为了保证安全会拒绝缩容或者疯狂后台复制数据成为“缩容地震”。存算分离没有这个限制节点说释放就释放。更进一步很多云厂商都提供竞价实例价格是普通实例的2-3折。存算分离架构下计算节点的无状态特性让竞价实例变得极其适用节点被回收任务自动重试换一批新的竞价实例继续跑。对容忍失败重试的离线批处理任务来说可以放心大胆地把大部分节点配成竞价实例保留下来的少量按量实例用于跑核心SLA任务。5.3 那些你会忽略的“隐性成本”存算分离不是只有好处有几处成本容易被忽视我提醒每个做改造的团队都注意跨AZ流量费计算集群和对象存储最好在同一个可用区跨AZ的数据访问需要收取流量费。这个费用虽然在对象存储场景下单价不高但大数据常驻任务会产生巨额流量一个月下来能到几万元。数据访问请求费对象存储通常按请求次数收费比如PUT/GET每万次多少钱。小文件越多请求次数越多费用越高。这又回到小文件治理——它不只是性能问题还是成本问题。冷数据回热费低频存储和归档存储的数据被访问时除了读取费还要付“数据取回费”。归档数据取回是按GB收费的如果归档策略写得太激进的“热”把经常要查的数据归档了那每次查一次账单都像割肉。我自己踩过的坑就是归档策略设置成了“60天未访问自动转归档”结果业务方在月末做历史同比分析一次性读取了大量归档数据那个月的取回费直接让账单翻倍。之后我把归档策略调成“180天未访问”并且对核心业务表的归档做了例外不再全局一刀切。6. 数据报表与可视化场景下的存算分离实践把“快”建立在正确的地方6.1 报表、大屏与存算分离的矛盾现在很多业务方做数据大屏、BI报表底层数据源就是大数据平台。存算分离架构对这类场景其实存在天然的“延迟矛盾”报表期望秒级响应而对象存储上的数据查询即便是Trino首次查询也需要秒级到十几秒的加载时间。我接触过不少做网约车、零售、物流这类业务的数据团队他们的数据大屏选型通常是FlaskECharts这类轻量方案大屏数据接口直接查数据仓库的聚合结果表。在存算分离架构下正确的做法绝不是让Flask接口去实时扫描对象存储里的明细表而是分层处理底层明细数据放在对象存储离线和准实时任务定时把结果写入聚合层。聚合层可以是一张存储在ClickHouse、Doris、StarRocks或MySQL中的结果表这些结果表数据量小、查询延迟低。Flask或Java后端只查询聚合层不直接触碰对象存储。这个分层看起来像是“又回到了以前的老路”但它在存算分离架构下效率极高明细层保留全部历史数据用于深度分析聚合层只占很小存储计算资源消耗低。换句话说存算分离不等于让所有查询都对着对象存储跑而是要把“扫描大数据”和“服务小请求”这两类事情拆开处理。6.2 物化视图与查询加速的常用组合对于报表场景我常用的一套组合是Iceberg表存明细所有DWD层明细数据落到Iceberg表存对象存储。Doris/StarRocks作为聚合查询层每天凌晨从Iceberg表同步聚合结果到Doris或者用Spark SQL直接写Doris。大屏接口只查Doris查询延迟控制在毫秒级无论底层明细数据有多大。这套组合既利用了对象存储的低成本又规避了它查询慢的问题。当然你会问为什么不直接把所有数据放Doris原因很简单成本。Doris这类OLAP引擎的多副本存储成本远高于对象存储明细数据放Doris存储费用会高出几个数量级。正确做法是热数据、聚合数据放查询引擎冷数据、明细数据放对象存储。6.3 定时调度的容错设计存算分离下任务挂了的救护措施最后聊一个踩坑经验。存算分离架构下计算集群如果完全弹性伸缩任务调度失败的场景跟传统HDFS完全不同。传统集群上任务挂了可以原地重试因为数据还在弹性集群一旦缩容挂掉的任务需要重新拉起一个集群才能重试。如果调度器没有设计好会出现“重试时集群已经缩没了任务卡死等集群”的尴尬。我们的做法是给所有定时任务加一个“弹性集群唤醒”机制调度器发现没有可用集群时先调用云API拉起一个指定规格的临时集群等集群Ready后再把任务提交上去。这里有两个经验值可以分享拉起一个10节点左右的EMR集群从提交到Ready大概3到5分钟。所以SLA要求10分钟内必须启动的任务必须提前把集群Hot Standby不能冷启动。如果大量任务都要“等集群”尽量把任务的启动时间错开10到15分钟避免同时唤醒多个集群造成资源浪费。对于报表和大屏这种固定SLA的任务我甚至建议保留一个常驻小集群专门服务核心报表不参与弹性缩容。毕竟大屏挂了是直接面向领导的宁可多花一点常驻成本也不能让大屏在关键时刻转圈圈。7. 从改造中提炼的经验清单哪些坑不必重复踩做完整套存算分离改造我有几条比较实用的经验写在这里当备忘也分享给准备踩这条路的团队。第一先在“影子环境”验证再动生产。影子环境不用真的复制一份完整数据可以只复制几张核心表关键是验证S3A配置、Hive Metastore指向、权限策略这几个“高危点”。我们当时跳过这步直接在生产环境小范围试跑结果权限策略配错了整个部门的人查询全部报403排查了整整一个下午。第二把对象存储访问参数调到生产级别。默认的S3A连接参数对大数据量并不友好比如连接池大小、最大连接数、上传分块大小这些建议按数据量级预先调整。举几个常用参数property namefs.s3a.connection.maximum/name value2048/value /property property namefs.s3a.threads.max/name value2048/value /property property namefs.s3a.fast.upload.buffer/name valuedisk/value /property property namefs.s3a.multipart.size/name value128M/value /property如果连接池太小数据量大时会出现“连接被拒绝”“超时重试”之类的诡异报错日志里看起来像是网络问题其实是参数没调到位。第三不要所有表都套同一个生命周期策略。不同表的数据热度和访问模式差异很大统一用“90天转低频”这种策略一定会在某个表上出事。建议按业务域拆分成不同的生命周期分组比如订单域热数据保留长一点、日志域热数据保留短一点。宁可运维上多写几条策略也别把所有表一锅炖。第四把“缓存预热”纳入调度日常。存算分离之后每天第一波查询是最慢的因为缓存都是空的。我们后来做了一个简单的“预热任务”每天凌晨在低峰期用Trino把当天会用到的主流报表查询跑一遍把热点文件拉到本地缓存。这样早上一上班业务方查数基本都是秒出体验稳定了很多。我做过好几套存算分离的改造实话讲这套架构确实能省下很可观的成本也让计算集群的弹性变得丝般顺滑但前提是按照它的脾气来设计任务和数据组织方式。如果只是把数据换个地方存查询反而更慢账单也不一定好看。最关键的还是回到架构本身让明细数据安安静静躺在低成本存储里让计算资源按需起落让查询引擎只碰它该碰的结果集——这样改造完整个平台的运行状态会清爽很多。最后再补充一个个人体会存算分离改造不是一次性的而是一个持续优化的过程。云厂商的对象存储性能、计算引擎的对接能力都在不断变化每隔半年把新特性拉通试一遍往往能再次省下一大笔成本。比如我们后来开了对象存储的生命周期和缓存预热能力改造后第一年只做了存储迁移第二年做了分层存储第三年又引入了物化视图每一轮优化都有实实在在的账单回报。如果你们正处在一个数据规模快速增长、机房成本压力越来越大的阶段存算分离是值得投入的长期方向。
返回列表