十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

存算分离架构深度解析:从原理到落地的关键技术与实践

存算分离架构深度解析:从原理到落地的关键技术与实践 存算分离这个词这几年在圈子里确实被反复提。无论是看云厂商的布道文章还是在群里聊数仓选型总绕不开它。我自己从早年搭Hadoop集群一路做过来经历过被数据本地性绑住手脚的阶段也踩过从存算一体迁到分离架构之后的各种坑对这个话题算是有切身体会。这个标题下的内容本质上是想搞清楚一件事计算和存储为什么要拆开拆开之后又该怎么用。这篇博文就围绕这个核心把架构原理、关键技术选型和落地实践一次说清楚读完你至少能明白自己接下来该关注哪些组件、在什么场景下值得动手改造。很多人在刚开始接触存算分离时容易陷入两个误区一是把它当成云厂商的新瓶装旧酒二是觉得只有超大集群才用得上。实际上这个概念从Hadoop时代就有雏形而真正让它普及的是对象存储的成熟和云上网络的发展。它解决的远不只是“存储成本”这一个问题还包括弹性、故障域、资源利用率等一连串痛点。所以这篇文章不只是讲什么是存算分离更会结合实操过程把迁移改造中容易被忽视的细节摊开来讲。1. 存算分离的本质把“食材仓库”和“后厨”分开想理解存算分离先从一个生活化的类比入手。早期的分布式计算架构很像一个大饭店里的每个厨师都自带食材仓库。每个后厨工位上都堆着自己的面粉、蔬菜和肉做菜的时候顺手就拿速度确实快但问题是有的工位食材堆成山有的工位却缺东少西仓库利用率很低更重要的是一旦某个厨师请假节点宕机他工位上囤的那堆食材就废掉了。如果你想让某个招牌菜从一天做100份扩到1000份还得先把食材分散到新加进来的厨师工位上去非常费劲。存算分离做的事情就是把这些食材仓库统一收走改成中央大冷库。所有厨师只管专心炒菜需要什么材料从冷库取就行。厨师队伍可以随时增减冷库容量也可以独立扩容二者互不拖累。数据领域里那个中央冷库就是共享存储层典型的是对象存储而厨师就是无状态的计算节点。这个类比能解释大部分架构设计动机但它掩盖了一个关键细节后厨和冷库之间那条“传菜通道”的带宽和速度如果不够厨师炒菜就会卡壳。这也是为什么存算分离在早期Hadoop时代不被看好的原因——那时的网络远没有现在这么可靠本地磁盘读取的速度又远快于跨网络拉取数据。现在情况不同了万兆、二十五万兆乃至更高带宽的网络已经很常见对象存储的吞吐能力也大幅提升“取食材”的代价已经下降到可以接受的范围。1.1 从数据本地性到解耦传统架构的痛点在传统Hadoop体系里有一个设计原则叫数据本地性Data Locality。HDFS会把文件切成块分散存储在不同的DataNode上而MapReduce或Spark在调度计算任务时会尽量让计算发生在“数据所在的机器”上以减少网络传输。这个设计在磁盘昂贵、网络带宽有限的年代是非常理性的选择也是Hadoop能够处理海量数据的基础。但成也萧何败也萧何。数据本地性带来一个隐含约束存储和计算被强绑定在同一批节点上。这就导致几个很实际的痛苦。第一扩容必须一起扩。你想加存储容量哪怕计算资源还绰绰有余也不得不采购一批带本地盘的机器反过来计算任务高峰期想加计算力也会把存储空间带着一起扩大实际上是白白烧钱。第二故障域很大。一台机器既跑计算又存数据一旦宕机不仅正在跑的任务失败还可能需要从其他副本恢复数据恢复过程对集群带宽和磁盘IO又是一个不小的冲击。节点多了以后这种“抻一下”的连锁反应会被成倍放大。第三资源利用率低。即使在同一集群内部有的节点磁盘快满了有的节点还空着有的节点CPU跑满有的节点闲置。你很难把这些资源在节点级别做精细调度因为数据和计算被锁死在一个物理边界里。这些痛点在小集群里还不明显数据量到PB级别后运维压力就开始成倍上升。我自己做过一个几十台物理机的CDH集群每次扩容都要提前一个月做采购申请扩容期间还要考虑数据拷贝和rebalance非常折腾。存算分离直接把“加存储”和“加计算”两个动作拆开从根本上消灭了这类问题。1.2 存算分离到底解决了什么问题如果要把动机压缩成几句话我认为存算分离解决了四个层面的问题。一是弹性伸缩。计算层可以做成无状态的随时拉起一批Spark或Trino节点跑任务跑完直接释放按量付费。这在离线数仓和临时分析场景里非常划算尤其适合中小团队。你不再需要维护一个常驻的、几十台机器的大集群去应对那每天只出现两三次的资源高峰。二是成本解耦。存储可以全部放到对象存储上按实际用量计费冷数据还有生命周期策略可以自动转归档。传统自建HDFS即使数据一年都不读那块盘的成本依旧在那里不会因为你不读就变便宜。三是故障隔离。计算节点不存数据坏了就丢直接替换存储层本身有跨可用区的冗余机制容错能力由存储服务兜底。整个系统的稳定性模型变得简单许多。四是多引擎共享数据。存算分离之后同一份数据放在对象存储上Spark可以读、Flink可以读、Trino可以读甚至Python脚本走pandas也能直接读。避免了过去“每个引擎一套数据副本”的尴尬数据一致性也好维护得多。当然存算分离不是没有代价。它把数据的“距离”拉远了计算引擎必须通过网络去拉数据遇到需要反复扫描大量数据的场景性能损耗会比本地读取更明显。这也是后续缓存层、数据布局优化这些配套技术存在的意义。2. 架构核心拆解存储、计算、元数据三位一体存算分离架构听起来像是在说“存储和计算分开”但真正落地的时候你会发现它远不止两层。一个能正常工作的存算分离系统至少包含三个核心层次存储层、计算层和元数据服务层。三层之间各司其职任何一层拉胯整个系统跑起来都会别扭。下面逐个拆开讲。2.1 存储层选型对象存储凭什么成了默认答案存算分离里的“存”实践中最常见的是对象存储比如公有云上的对象存储服务或者自建的开源对象存储系统如MinIO。为什么不是HDFS也不是传统的SAN存储这里有个选型逻辑问题。先看对象存储的优势。它几乎是无限扩展的你不需要操心容量上限它的成本相对低廉尤其适合海量冷数据它天然跨地域冗余可用性有保障它还有标准的S3接口几乎所有大数据引擎都原生支持兼容性不是问题。这些特性叠加起来让对象存储几乎成了存算分离的“标配”。再看HDFS的角色。HDFS本身不是不能在存算分离架构中出现——在一些私有化部署场景里会把HDFS继续作为共享存储层使用计算节点通过HDFS客户端远程访问。这在技术上可行但HDFS依然有NameNode单点瓶颈、扩缩容需要人工运维等问题并非理想的共享存储。它更适合被当作“遗留资产”来兼容而不是新架构的首选。还有一个选项是云硬盘或共享文件系统比如给每台计算节点挂载一块网络磁盘。这种方案的IO表现更好但成本高、扩展上限有限通常只适合特定的存算分离需求比如跑数据库服务。对于大数据分析这类海量扫描场景对象存储的性价比无可替代。选存储层的时候我个人的经验是先确认三个问题你要不要用到ACID事务能力比如配合Iceberg、Hudi做实时入湖你的工作负载是扫描密集型还是点查密集型以及你是否接受对象存储的秒级一致性问题。这三个问题的答案基本就决定了你在对象存储和大数据文件系统之间怎么选。2.2 计算层重构无状态化与弹性伸缩存算分离架构下计算层需要做到真正的“无状态”。所谓无状态不是说计算节点上没有任何数据残留而是说计算节点不承担持久化存储的职责。任务跑完本地临时数据可以全部清空节点被销毁不影响任何系统状态。要做到这一点计算引擎要能直接从远端存储拉取数据并且做好分片、并行和容错。以Spark为例在存算分离架构下HDFS的数据本地性调度策略基本失效取而代之的是“就近拉取”策略即每个Executor尽量从距离自己最近的可用区读取数据。实践里Spark读写对象存储的性能瓶颈往往不在引擎本身而在文件发现和元数据请求的并发量上。无状态化带来的最大收益是弹性伸缩变得非常简单。我见过一个典型的离线分析场景白天低峰期只保留一个4节点的Trino集群到了晚上数据跑批之前通过脚本自动扩容到16个节点跑完再缩回去。整个过程不需要迁移任何数据也几乎没有启动预热成本。这在存算一体的时代是不可想象的。当然无状态计算也不是没有代价。它在每次任务启动时都少了一层“本地数据预热”的缓存优势如果任务反复扫描同一批数据性能上会吃亏。这就是为什么很多系统会引入缓存加速层比如Alluxio或依赖对象存储的缓存网关。这部分后面细说。2.3 元数据服务与数据编排容易被忽略的“大脑”很多人以为存算分离就是把文件扔到对象存储上然后随便找个引擎去读。真这么做大概率会遇到一个尴尬情况表结构得自己记分区信息得自己维护数据文件损坏了也无从知晓跨引擎访问同一份表时各方对schema的理解还对不上。元数据服务就是来解决这个问题的。在大数据生态里最基础的元数据服务是Hive Metastore它负责记录表、分区、字段、文件路径等基本信息。你可以不使用Hive引擎本身但几乎所有引擎Spark、Trino、Flink都会对接Hive Metastore。更现代的方案是用Iceberg、Hudi或者Delta Lake的Catalog它们除了记录元数据还能管理事务、快照和表结构演进。元数据服务相当于整个存算分离系统的“大脑”。它让计算引擎能够在不知道存储细节的情况下把“一张逻辑表”映射到“一堆物理文件”上。没有它存算分离就只是一堆散落的数据文件而不是一张张可以被SQL查询的表。在实际部署时元数据服务的选择会影响很多东西。自建Hive Metastore虽然免费但高并发下会成为瓶颈需要做高可用部署主备或联邦。云上的托管Catalog比如各类云数据湖服务省心很多但会带来一定程度的厂商绑定。我的建议是如果你已经重度使用某个云厂商生态优先用托管Catalog如果追求跨云和多环境的灵活性就基于开源Iceberg加自建Catalog来设计。2.4 配套组件缓存与数据布局优化存算分离真正的难点不在“把数据放到对象存储上”而在于“如何让频繁访问数据的性能不输给本地读取”。这里就需要配套组件来兜底其中缓存层是最重要的一个。缓存加速的思路简单说就是在计算节点和对象存储之间加一层高性能缓存把热数据缓存到本地磁盘或内存里下次再读就命中了。Alluxio是这层最常见的开源方案它提供一个兼容HDFS接口的虚拟文件系统底层对接对象存储上层对计算引擎透明。云厂商也有类似服务比如各类“数据加速器”或“缓存网关”。但缓存也不是银弹。缓存命中率取决于工作负载的数据访问模式。如果任务每天全量扫描一批全新的数据缓存基本帮不上忙反而多了一层资源消耗。如果用得准比如报表查询、BI分析这类对同一批维度表反复查询的场景缓存命中率会很高性能提升非常明显。除了缓存数据布局优化也是提升存算分离性能的关键。对象存储上如果存在大量小文件计算引擎在列出文件列表和获取元数据时就会非常慢。最常见的治理手段是小文件合并比如用Spark定期把几千个小parquet文件合并成几百个较大的文件分区设计也尽量做到按时间或业务维度切分方便引擎做分区裁剪。3. 典型应用场景与落地实践知道了架构原理下一步就是看它到底能用在哪里。存算分离并不是一个为了炫技存在的架构模式它有几个非常典型的应用场景在这些场景里它对比存算一体化架构的优势会被放大得很明显。3.1 云数仓与湖仓一体Snowflake、Redshift与托管数仓的启示云数仓可能是大众最熟悉的存算分离案例。Snowflake从诞生起就是典型的存储、计算分离架构存储层用对象存储计算层由多个独立的虚拟仓库Virtual Warehouse组成彼此之间通过一层共享元数据做协调。你可以同时开好几个计算仓有的跑BI有的跑数据工程它们共享同一份数据却互不抢占计算资源。这也是Snowflake能实现“按需启停计费仓”的基础。Redshift早期是典型的本地存储架构正因为感受到了存算分离的浪潮压力后来推出了Redshift Spectrum这类功能——让查询引擎直接扫描S3上的文件。国内云厂商的数据仓库服务比如阿里云MaxCompute、华为云服务等在设计上也大量借鉴了存算分离思路把存储和计算拆开计费让用户可以独立扩缩容。对这些云数仓来说数据一旦进入对象存储层就成为整个企业的“共享数据底座”。数仓、数据湖、机器学习平台都在同一份数据上工作避免了传统数仓让数据反复导入导出的窘境。如果你正在选型云数仓可以重点考察它的存储和计算是不是真正解耦的解耦程度决定你后续的弹性空间和成本结构。3.2 离线批处理与弹性调度的组合拳离线批处理是存算分离落地最顺的场景。传统离线数仓每天都有固定的跑批任务高峰期集中、低峰期大量资源闲置。如果按峰值配置常驻集群成本浪费非常明显如果按均值配置高峰期又容易排队。存算分离架构把这个问题改写成了一道资源调度题存储层常驻计算层按任务量弹性伸缩。你可以给Spark或Flink作业配置一个动态资源池跑批开始前自动拉起足量Executor跑完之后自动释放。在云环境下这可以做到“用完即走”账单上实实在在地省下一笔钱。我自己实践过的一个案例是一套原来需要固定20台机器跑6个小时的离线任务迁到存算分离后高峰期临时扩容到40个计算单元跑批耗时压缩到2.5小时。虽然计算成本差不多但任务时效大大提升而且低峰期集群可以直接缩到最小规模。对于有“早上九点前要出报表”这种硬性时效要求的团队这种弹性能力非常有价值。3.3 实时数据管道与AI训练的数据底座存算分离的另一个典型场景是实时数据管道。过去Flink做实时计算状态和checkpoint往往落在本地或HDFS上管理和扩容都不方便。在存算分离架构下Kafka的数据持续写入对象存储形成一套可重放的数据湖底座Flink从对象存储读取增量数据做实时加工结果表再写回湖里。整个链条变成“对象存储-实时引擎-对象存储”中间不再需要为实时任务专门维护一套独立存储。这个方案最大的好处是统一了批和流。离线任务和实时任务访问的是同一份数据、同一个Catalog下的同一张表数据的口径很容易对齐不会出现“离线算出来的GMV和实时看板对不上”这种经典困境。AI训练场景也是存算分离的重要受益者。深度学习训练需要海量数据但并不是所有样本在每一轮训练中都会被读到。把训练样本放在对象存储上训练节点按需拉取Batch数据配合数据预取和内存缓存可以让大批训练任务轻松跑起来。由于计算节点不带数据你可以同时为一个训练任务申请数百张GPU卡训练结束全部释放成本和扩展性都得到极大优化。4. 实施路径与避坑实录讲了这么多理念最后落到实操。从传统Hadoop架构迁移到存算分离或者从零搭建存算分离架构到底该怎么做这里我给出一个经过多次实践验证的执行框架以及在过程中最容易踩的坑。4.1 从传统集群迁移到存算分离的五步法第一步是评估现状。梳理现有数据规模、表数量、分区分布、计算任务类型和资源消耗曲线。这一步的目标是搞清楚哪些数据适合先迁、哪些应该留在原地以及计算出迁移到对象存储后的大致存储成本用具体数字支撑后续决策。第二步是选择存储与元数据底座。如果是从零搭建议直接用对象存储加Iceberg或Hudi作为数据湖底座元数据用对应的Catalog。如果是云上迁移优先考虑云厂商托管的数据湖服务能节省大量运维成本。存储桶的结构建议按业务域划分例如一层为“数据湖根目录”二层区分业务域三层按数据分层如原始层、明细层、汇总层不要拍脑袋乱建路径。第三步是元数据迁移。把Hive Metastore里的表结构、分区信息、字段注释完整迁移到新的Catalog。这一步比想象中繁琐尤其当表数量上千张、分区数量上万时手工迁移不现实建议写脚本批量导出再导入同时做好schema一致性校验。迁移前建一张“元数据映射表”记录新旧表的对应关系后续出问题方便排查。第四步是数据迁移与校验。数据复制本身不难用分布式数据迁移工具或者Spark作业把HDFS的数据拷贝到对象存储即可。真正重要的是校验源和目标文件数量要一致文件大小要一致抽样对比数据记录数和关键字段哈希值确保数据零丢失。我在迁移中踩过最常见的坑是一些特殊字符命名的文件在对象存储上显示异常这类边界情况靠抽样很难发现必须全量跑一遍“文件清单比对”。第五步是任务改造与双跑验证。把Spark、Hive、Presto等作业的数据源路径从HDFS切到对象存储这个过程通常需要改配置和部分SQL脚本因为有些SQL里的UDF对存储系统内部路径有隐式依赖。最稳妥的方式是“双跑”一段时间新老架构同时跑同一批任务对比产出结果确认一致后再彻底切换。4.2 生产环境常见问题速查表按经验存算分离上线后的一个月是最容易出问题的时候。我把实际运维中遇到的高频问题整理成一张速查表方便你快速定位。现象可能原因排查思路与解决方向查询性能比原来慢很多缓存命中率低或小文件太多导致元数据开销大先看缓存命中率监控再看任务扫描文件数。优化方向是小文件合并和分区裁剪任务偶发报“文件不存在”对象存储最终一致性读到了尚未同步的旧快照查询引擎开启“只读最新快照”配置或使用支持强一致的Catalog任务失败后重试通常能解决元数据服务成为瓶颈查询并发高Hive Metastore扛不住给Metastore加缓存或扩成集群模式最好的办法是换用更现代的Catalog服务弹性扩容后任务变慢计算节点跨可用区拉取数据网络开销变大调整数据缓存的亲和性尽量让计算任务调度到离数据更近的节点上账单超出预期数据扫描量过大或忘记配置冷热分层策略检查是否每个任务都在做全表扫描给冷数据配置生命周期转为低频或归档存储数据重复或丢失写入对象存储的操作没有走事务表格式确认所有写入操作都通过Catalog接口如Iceberg而不是绕过Catalog直接写文件这些问题里最值得多说一句的是第一项。很多团队迁到存算分离后发现跑批变慢第一反应是“架构选错了”其实大概率是数据没治理好。对象存储上的小文件如果超过一定量级比如某个分区下有几千个文件引擎在文件发现阶段就会耗时数十秒甚至数分钟这部分开销在HDFS时代是感觉不到的。所以每次迁移完第一件事就是把历史小文件合并一遍然后再观察性能这个经验我在多个项目中反复验证过。另一个容易被忽视的坑是权限模型。传统HDFS的权限体系里POSIX权限和文件路径强绑定对象存储的权限是基于Bucket策略和IAM角色的两种体系差异很大。如果事前不做好权限映射设计迁移后很容易出现“有的同事访问不了表有的同事能下整个目录”这种混乱情况。建议在迁移之前就按业务角色梳理数据访问边界在对象存储和Catalog层同时配置好权限。5. 最后的一点个人体会做了几年存算分离架构的落地我最大的感受是它更像一种“架构风格”而不是一个“组件”。它背后反映的是当存储和网络的成本降低到一定程度后我们可以重新设计计算系统里资源组织的方式——让架构更弹性、更灵活而不是永远围绕“数据放本地”这个旧时代的约束来设计。如果你所在的团队还在传统Hadoop集群上挣扎我的建议是先不要盲目跟风上存算分离。把当前的难点列出来看看它是否真的是存储、计算绑定导致的。如果只是任务慢、SQL写得烂那换架构也救不了你。但如果你的痛点确实是弹性不足、扩容成本高、数据多引擎共享难那存算分离就值得认真考虑。从实施节奏上我也建议从业务侧挑一个数据量适中、价值明显的场景先做试点跑通后再逐步扩大范围。迁移的过程虽然繁琐但只要元数据、数据校验、权限设计这三件事做好了后续基本就是水到渠成的事。存算分离不是终点它只是让数据架构拥有了更大的可塑性至于能变出多少花样就看你怎么用了。
返回列表