面向存算分离实时分析平台的对象存储技术要求
存算分离已经成为实时分析平台的默认选择:计算节点无状态、按需伸缩,数据统一落在对象存储上,容量与算力各自独立计价。收益确定,代价也确定——原先发生在本地 NVMe 上的读写,全部改由网络往返完成。
StarRocks 从 3.0 版本引入存算分离形态,BE 被无状态的 CN(Compute Node)取代,数据以 segment 文件存放在对象存储或 HDFS 上,本地盘退化为缓存层。对象存储因此从“归档仓库”变成“在线数据通路”:每一次查询扫描、导入提交和后台合并都要经过它。
存算分离实时分析平台场景下,对象存储的选型标准必须从“容量与单价”切换为“带宽、并发、尾延迟、一致性、元数据规模”五项指标。前两项决定 MPP 算力能否用满,中间两项决定高并发查询的体感,最后一项决定平台能长到多大。
一、StarRocks 的定位与技术特征
StarRocks 是面向实时分析的 MPP OLAP 数据库,采用 FE 与 BE 分离架构:FE 负责元数据管理、查询解析与调度,BE 负责存储与计算执行。
在实时分析平台中,它同时承担两个角色:一是实时数仓,Flink、CDC、Stream Load 写入内表,查询秒级可见;二是湖仓查询引擎,通过 External Catalog 直接读取对象存储上的 Iceberg、Hudi、Delta Lake、Hive 表,不做数据搬迁。与 Trino(无自研存储的查询引擎)和 Snowflake(托管闭源服务)不同,StarRocks 常以私有化或专有云形态部署在用户自己的对象存储之上。
StarRocks 技术上具有以下优势:
表 1 StarRocks 两种部署形态的对比
| 维度 | 存算一体(shared-nothing) | 存算分离(shared-data) |
|---|---|---|
| 节点角色 | FE + BE,存储与计算同进程 | FE + CN,CN 无状态,只做计算与缓存 |
| 数据位置 | BE 本地盘,多副本 | 对象存储或 HDFS,单份权威数据 |
| 本地盘职责 | 数据主副本 | 热数据缓存(Data Cache) |
| 扩容方式 | 加节点触发数据重平衡 | 加 CN 无需迁移数据,秒级生效 |
| 成本结构 | 按计算节点容量买单,冷数据也占 SSD | 按实际用量计费,冷热可分层 |
| 典型读路径 | 本地 NVMe,亚毫秒 | 缓存命中接近本地;未命中走网络 |
| 适用场景 | 高并发点查、强实时、数据量可控 | 存量大、冷热分明、弹性需求强 |
二、实时高并发 OLAP 的 IO 模式特征
实时高并发 OLAP 的 IO 由三条性质不同的路径叠加:查询读、实时写、维护写,三条路径同时压在同一套对象存储上。

2.1 查询读路径:小随机读与并行大扫描
元数据层以 Iceberg 为例是分层结构:catalog 指向 metadata JSON,快照指向 manifest list,再指向一批 manifest,manifest 里才是数据文件与删除文件的清单和统计信息。一次查询要逐层读完这条链,才能做分区裁剪与文件级跳过;单个 manifest 几十 KB 到几 MB,但一张表可能引用成百上千个。Delta Lake 则集中在 _delta_log 目录,靠 checkpoint 加增量 JSON 回放。这一层的共同特征是:请求很小、次数很多、QPS 很高、对首字节延迟极其敏感——它决定了查询的起始速度。
数据层是高度并行的大顺序读:引擎把文件切成扫描任务分发到各节点,每个 Parquet 或 segment 文件先读一次 footer,再按列与行组发起 Range 读取,配合谓词下推只取所需。聚合带宽决定扫描吞吐,并发连接数决定带宽能否吃满,尾延迟决定单个查询会不会被拖住。行级删除通过 delete 文件或删除向量表达,这类文件小而多,读放大体现在请求数上,存储侧对小 IO 延迟与单目录对象数因此变得敏感。
2.2 实时写路径:高频小批量提交
流式写入按十秒到分钟级的提交间隔推进:每次提交写一批几 MB 到几十 MB 的数据文件,再写新的元数据,通过一次原子指针切换完成版本发布。对象存储看到的是大量 PUT、写新不覆盖、文件写完即不可变。提交频率越高,小文件与元数据文件越多,后续查询的规划开销越大。
2.3 维护写路径:Compaction 与清理
Compaction 读一批小文件、合并排序后写出少数大文件,带宽占用与一次全表扫描相当,安排在业务高峰就会与在线查询争抢。快照过期与孤儿文件回收则在千万级对象目录上制造 LIST、HEAD、DELETE 的元数据操作风暴。两者不产生业务价值,但缺了它们,前两条路径会在几天内退化。
三、以 StarRocks 存算分离为例的读写 IO 模式
存算分离形态下,CN 不再持有权威数据,每次读都要先问本地缓存、再问对象存储;每次写都要落本地盘并异步上传对象存储,同时把元数据写到一个共享的元数据服务。
写入路径上有一个关键设计:数据先写本地缓存盘,再异步上传对象存储。这让导入的可见延迟不受对象存储单次 PUT 时延的直接影响,代价是对象存储上的数据会滞后于本地,且 CN 故障时未上传的部分需要重放。为了让“版本切换”在对象存储上是原子的,StarRocks 采用批量发布版本的方式,把多个事务产生的 tablet 元数据合并后一次提交,减少元数据写入次数与提交冲突。
这意味着存算分离并不要求对象存储具备极低的单次写延迟,但对写吞吐、元数据写入的原子性与提交冲突率要求非常高。读路径则相反,它对单次读延迟高度敏感,因为缓存未命中时这段延迟会直接进入查询关键路径。

3.1 内部表查询读
StarRocks 的 Primary Key 表使用“Delete+Insert”存储引擎:数据写入时,通过主键索引和 DelVector 把旧记录标记为删除,新记录写入新的数据文件;查询时只需读最新版本即可,避免 merge-on-read 的在线多路归并。对对象存储的 IO 压力分三块:
3.2 湖表查询读
湖表查询经由 External Catalog 读取元数据与表格式快照,再由 CN 扫描 Parquet。StarRocks 在这一层做了元数据缓存与 Data Cache 两级缓存。元数据层会产生高 QPS 小随机读,数据层会产生大吞吐 + 高并发顺序读。在缓存预热(CACHE SELECT)、大量随机查询、或缓存失效时,仍会出现对对象存储的突发读请求。
3.3 实时写入与版本提交
写入主键表的导入按事务组织:数据按分区与分桶分发到 tablet,写成新的 rowset(多个 segment 文件);提交阶段更新主键索引、把旧记录标记进删除向量、生成新版本元数据。对对象存储而言,这是一个“大量并发 PUT + 一批小对象写”的组合。
在存算分离架构下,主键索引的持久化位置也影响 IO 特征。索引放在对象存储,则每次写入都会引入额外的小随机 IO。
3.5 Compaction 与 Vacuum
类 LSM 的多版本存储必然产生合并压力。FE 按分区的 compaction score 调度任务,CN 读一批旧 rowset、合并排序去重、写出新的 rowset、提交新版本,旧版本在不再被引用后由 Vacuum 清理。
这条路径对存储的压力是双份的:合并本身要扫描大量旧文件(并发 Range GET)并写出新文件(大 PUT),清理阶段还要遍历目录、校验引用、删除对象。
3.4 缓存命中率决定性能上限
StarRocks 官方 SSB 1 TB 基准(1 FE + 5 CN,v3.3.0)显示:开启 Data Cache 的存算分离集群 13 条查询总耗时 83,444 ms,存算一体 100,406 ms,关闭缓存则放大到 238,412 ms——缓存全热时存算分离比存算一体快约 20%,全冷时慢一倍以上。京东物流的生产数据更接近真实:5 个 CN、缓存 TTL 20 天,命中时 P95 与 P99 查询延迟均小于 10 秒。
因此,存算分离平台的性能由命中率与回源能力共同决定:前者靠引擎侧的缓存设计,后者完全由对象存储的带宽、并发与尾延迟决定,两者不能互相替代。
四、存算分离架构下,对象存储的技术要求
4.1 核心命脉:吞吐与一致性
• 高吞吐带宽与高 IOPS 能力(重要性:★★★★★)
MPP 引擎在执行查询时,采用的是“大规模并行扫描”策略。一个大表的查询会被切分到多个节点,每个节点又会启动数十个线程并行读取数据。如果是高并发场景,可能有成百上千个查询同时扫描同一份数据。具体需求:
高吞吐:对象存储必须能提供极高的聚合带宽(例如单集群 Tbps 级别),以免成为 MPP 引擎的数据供给瓶颈。
高 IOPS:现代湖仓格式(如 Parquet)虽是大文件,但写入时会生成大量小文件(元数据文件、Bloom Filter、deletes 文件等),且查询时需要高频读取元数据。存储系统必须支持高并发的 HEAD/GET/PUT 操作,通常需要达到数十万甚至百万级 QPS。
如果存储系统有带宽上限或单节点 IOPS 限制,MPP 引擎的 CPU 利用率会极低,查询耗时主要卡在“IO Wait”。
• 强一致性保证与原子性操作(重要性:★★★★★)
“实时湖仓”意味着数据在频繁写入、更新,同时被并发查询。湖仓格式的核心(如 Iceberg/Delta/Hudi)依赖“元数据指针”来管理快照。如果存储层没有一致性保证,会导致查询读到脏数据或写入损坏。具体需求:
Read-After-Write Consistency(写后读一致性):数据写入并提交后,所有后续的读取必须能立即看到最新数据。某些对象存储默认有最终一致性延迟,这对实时分析是致命的。
Atomic Rename/PUT(原子操作):湖仓的事务提交通常通过“原子性重命名”或“条件更新”文件来实现。对象存储必须支持原子的 Rename 操作或 S3 的 PutObjectIfMatch(条件写入)语义。
如果缺乏强一致性,可能会出现“刚写入的数据查不到”或者“并发写入导致元数据损坏”,这对于金融风控、实时大屏等业务是不可接受的。
4.2 性能决胜点:尾延迟与小对象元数据
• 低且稳定的延迟(重要性:★★★★☆)
高并发 OLAP 意味着大量短查询(如 Dashboard 刷新、即时查询)。这些查询对 P95/P99 延迟极其敏感。MPP 是同步执行模型,整个查询的耗时取决于“最慢的那个节点”。具体需求:
尾部延迟控制:存储系统的响应时间不能有巨大的长尾抖动。如果 1% 的请求因为 GC(垃圾回收)、锁竞争或网络抖动导致 500ms 延迟,在 MPP 架构下,这会拖慢整个查询。
元数据操作低延迟:Listing 操作(列出文件列表)必须快。湖仓引擎启动查询的第一步就是解析元数据,如果 List 操作慢,首屏延迟会很高。
延迟抖动会导致 MPP 引擎频繁超时或重试,极大消耗系统资源,严重时导致服务不可用。
• 高效的小文件处理能力(重要性:★★★★☆)
实时入湖会产生大量小文件(流式写入、Upsert 产生的 Delete 文件)。虽然湖仓引擎侧会做 Compaction(合并),但存储侧依然面临挑战。具体需求:
优化的小 IO 性能:存储系统不应因文件小而导致性能骤降。
快速的 Listing/Head 操作:千万级文件目录下的遍历速度必须极快。传统的文件系统在目录层级深、文件数量大时,Listing 性能会指数级下降,这对湖仓是严重阻碍。
如果对象存储处理小文件慢,会导致查询规划阶段耗时极长,甚至 OOM(内存溢出)。
4.3 成本与运维:存储分级、生命周期与生态接口
• 存储分级与生命周期管理(重要性:★★★☆☆)
湖仓的数据量通常是 PB 级起步。高并发查询往往只涉及“热数据”(如最近 3 天),而大量“冷数据”(如历史归档)占用昂贵的高性能存储是不经济的。具体需求:
自动分层:支持将不常访问的数据自动迁移到低成本存储层(如 S3 Glacier、OSS 低频存储),且对上层引擎透明。
智能缓存:对于私有化部署的对象存储,最好能提供分层存储(高性能 SSD 层 + 大容量 HDD 层)或与计算侧缓存(如 Alluxio)配合。
如果不具备分级能力,企业往往因为无法承担存储成本而限制数据入库,导致数据湖变成“数据沼泽”。
• 生态接口兼容性(重要性:★★★☆☆)
主流的 MPP 引擎和表格式都基于 S3 协议开发。具体需求:
S3 API 兼容:必须完全兼容 S3 接口,否则引擎对接成本极高(如 HDFS 接口已逐渐边缘化)。
特殊功能支持:支持 S3 Select(谓词下推到存储层)、多部分上传等特性,以支持流式写入和性能优化。
表 2 对象存储技术要求优先级
| 优先级 | 技术要求 | 对应的 IO 模式 | 不满足时的后果 |
|---|---|---|---|
| P0(基石) | 高吞吐 & 高 IOPS | 带宽、并发 QPS | 避免 MPP 引擎“空转”,保障全表扫描性能 |
| P0(基石) | 强一致性 & 原子性 | Read-After-Write,Atomic Rename | 保障事务 ACID,防止并发读写导致数据损坏 |
| P1(性能) | 低延迟 & 低抖动 | P99 延迟,Straggler | 保障高并发查询的响应速度,避免拖尾效应 |
| P1(性能) | 海量小文件处理 | 快速 List,元数据性能 | 解决实时流写入带来的“小文件风暴”问题 |
| P2(成本) | 分级存储 & 低成本 | 生命周期管理,冷热分层 | 降低 PB 级数据的 TCO(总拥有成本) |