StarRocks 存算分离(Shared-Data)集群 FAQ 全解析:建表、缓存、Compaction 与对象存储排障实战
【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks
本文以 StarRocks 官方 FAQ 文档《Shared-data》为基础,结合当前仓库中 FE/BE 的真实源码与默认配置,系统梳理存算分离(shared-data,又称 lakehouse 云原生)集群中 13 类高频问题:从建表失败与建表缓慢、Drop Table 后对象存储数据残留、查询变慢的四大根因,到 Compaction 卡死清理、跨 Warehouse 压缩缓慢、存储用量虚高、"meta does not exist" 以及小文件膨胀等。读完本文,你将掌握一套可复制的诊断路径——包括SHOW PROC、Query Profile 指标解读、ADMIN SET FRONTEND CONFIG动态调参——并理解每个配置项在 FE/BE 源码中的实际作用点,从而在真实集群中快速定位并解决问题。
一、总览:Shared-data 集群常见问题的分类地图
与存算一体(shared-nothing)架构不同,shared-data 集群将数据文件与元数据存放于对象存储(S3、OSS、GCS 等),BE/CN 无本地持久数据。这一架构带来弹性伸缩、存储与计算分离等优势,同时也引入了新的故障面:对象存储延迟与可用性、数据缓存(Data Cache)、远端读放大、元数据生命周期(Compaction + Vacuum)等。
原文档涉及的问题可归为五类:
| 类别 | 涉及问题 |
|---|---|
| 建表链路 | 建表失败、建表耗时过长 |
| 数据生命周期 | Drop 后数据不清理、存储路径定位、存储用量虚高、小文件过多 |
| 查询性能 | 查询缓慢(缓存未命中、Compaction 不足、Tablet 设置不当、datacache.partition_duration不当)、Warehouse 下全量超时 |
| 写入性能 | 高频导入变慢 |
| 后台任务 | Compaction 卡死、K8s 中 Compaction 慢、查询报 "meta does not exist" |
下文按此框架逐一展开,并给出可执行的排查命令与参数依据。
二、建表问题
2.1 为什么建表会失败?
建表失败时,第一步是查看 BE 日志(be.INFO)定位确切原因。常见的失败原因有三类:
- 对象存储配置错误:例如
aws_s3_path、endpoint、认证信息(authentication)配置不正确,导致 BE/CN 无法访问目标存储桶。 - 对象存储服务不稳定或异常:存储侧出现瞬时错误、限流或区域网络抖动,建表过程中写入 Tablet 元数据失败。
- 存储卷(Storage Volume)连通性校验失败:shared-data 模式下,执行
CREATE STORAGE VOLUME或ALTER STORAGE VOLUME时,FE 会校验存储的可访问性,该校验由 FE 配置enable_storage_volume_access_check控制(默认开启)。
该校验逻辑的源码实现在 StorageVolumeMgr.java:仅当集群处于 shared-data 模式(RunMode.isSharedDataMode())且enable_storage_volume_access_check为true时,才会调用StorageVolumeAccessChecker.check(name, svType, locations, params)真正探测存储连通性。配置项默认值定义于 Config.java:
public static boolean enable_storage_volume_access_check = true;一个值得注意的细节(来自 StorageVolumeMgr.java):ALTER STORAGE VOLUME时,只有影响连通性的属性发生变化(如云凭证、endpoint 等)才触发访问检查;仅修改enabled、comment等元数据属性时跳过检查——这样即使存储暂时不可达,运维人员仍可以先将卷禁用。
如果关闭该检查(ADMIN SET FRONTEND CONFIG ("enable_storage_volume_access_check" = "false")),校验将被跳过,但这也意味着配置错误会在真正读写数据时才暴露,建议生产环境保持默认开启。
其他报错场景:若收到Error 1064 (HY000): Unexpected exception: Failed to create shards. INVALID_ARGUMENT: shard info cannot be empty,通常是使用了自动分桶推断(automatic bucket inference),但当时没有任何 CN 或 BE 节点存活,导致无法推断分桶信息。该问题已在 v3.2 修复,遇到时请先确认计算节点已注册且健康,再重试建表。
2.2 为什么建表耗时过长?
建表慢的根源通常在于分桶数量过多(尤其是分区表)。StarRocks 会为每个 Tablet 在对象存储中写入一份 Tablet 元数据文件,而对象存储的单次写入延迟(LIST/PUT 延迟)远高于本地盘。当桶数 × 分区数很大时,成千上万次高延迟的元数据写入会显著拉长总建表时间。
建议从三方面入手:
- 减少分桶数量:合理评估数据量,避免过度分桶(分桶数估算公式见下文 4.3 节)。
- 增大建表线程池:调整 BE 配置
create_tablet_worker_count,其默认值为3,定义于 config.h。该参数控制创建 Tablet 的工作线程数,调大可提升并发建表吞吐。 - 排查对象存储写延迟:确认存储侧是否存在限流、热点或网络抖动,必要时联系存储服务方或切换 region/endpoint。
三、数据清理与存储路径
3.1 Drop 表后,对象存储中的数据为什么没有清理?
shared-data 模式下 StarRocks 支持两种删除模式,二者语义不同:
DROP TABLE xxx:仅将表元数据移入 FE 回收站(Recycle Bin),数据并不删除,便于误删恢复。DROP TABLE xxx FORCE:立即删除表元数据与对象存储中的数据。
如果执行过删除但数据仍残留,请依次检查:
- 是否使用了
DROP TABLE xxx FORCE(普通 DROP 不会立即清理)。 - 回收站保留参数是否设置过大:
- FE 配置
catalog_trash_expire_second:控制回收站中元数据的保留时长,默认86400秒(1 天),见 Config.java; - BE 配置
trash_file_expire_time_sec:控制 BE 端垃圾文件过期时间,默认86400秒,见 config.h。
- FE 配置
- FE 日志中是否存在删除失败记录(例如 RPC 超时)。若是 RPC 超时,可适当调大相关 RPC 超时配置后重试删除。
3.2 如何定位表数据在对象存储中的路径?
执行以下 SQL 即可获取表的存储路径:
SHOW PROC '/dbs/<database_name>';原文档中的示例输出如下:
mysql> SHOW PROC '/dbs/load_benchmark'; +---------+-------------+----------+---------------------+--------------+--------+--------------+--------------------------+--------------+---------------+--------------------------------------------------------------------------------------------------------------+ | TableId | TableName | IndexNum | PartitionColumnName | PartitionNum | State | Type | LastConsistencyCheckTime | ReplicaCount | PartitionType | StoragePath | +---------+-------------+----------+---------------------+--------------+--------+--------------+--------------------------+--------------+---------------+--------------------------------------------------------------------------------------------------------------+ | 17152 | store_sales | 1 | NULL | 1 | NORMAL | CLOUD_NATIVE | NULL | 64 | UNPARTITIONED | s3://starrocks-common/xxxxxxxxx-xxxx_load_benchmark-1699408425544/5ce4ee2c-98ba-470c-afb3-8d0bf4795e48/17152 | +---------+-------------+----------+---------------------+--------------+--------+--------------+--------------------------+--------------+---------------+--------------------------------------------------------------------------------------------------------------+ 1 row in set (0.18 sec)注意输出中Type列为CLOUD_NATIVE,这正是 shared-data 模式下表类型的标识。StoragePath列展示s3://<bucket>/<前缀>/<uuid>/<table_id>形式的根路径。
目录结构随版本有差异:
- v3.1.4 之前:表数据散落在单一目录下。
- v3.1.4 起:数据按分区组织。
StoragePath显示的是表根路径,其下包含以Partition ID命名的子目录;每个分区目录内又分为data/(存放 Segment 数据文件)与meta/(存放 Tablet 元数据文件)两个子目录。
理解这一结构有助于你核对对象存储中的文件布局、判断数据是否异常残留,或为手工清理提供依据。
四、查询性能问题
4.1 为什么 shared-data 集群中查询会变慢?
shared-data 集群查询变慢的常见原因有四类:缓存未命中(Cache miss)、Compaction 不足导致小文件过多、并行度不佳(Tablet 过少或分桶倾斜)、datacache.partition_duration设置不当导致缓存失效。
诊断的第一步永远是分析 Query Profile,用数据而非猜测定位根因。下文针对每类原因给出具体的 Profile 指标与处置手段。
4.2 缓存未命中(Cache Miss)
shared-data 集群的数据存于远端对象存储,Data Cache(数据缓存)是查询性能的生命线。若查询突然变慢,优先检查 Query Profile 中的两个关键指标:
CompressedBytesReadRemote:从远端读取的压缩字节数(非零说明发生了远端读);IOTimeRemote:远端 I/O 耗时。
缓存未命中的常见成因:
- 建表时关闭了 Data Cache:检查建表属性中缓存开关是否被显式关闭。
- 本地缓存空间不足:缓存被淘汰率过高,热数据无法驻留。BE 侧缓存相关配置定义于 config.h:
datacache_enable(默认true)、datacache_mem_size(默认20%,即内存的 20%)、datacache_disk_size(默认100%,即磁盘剩余空间的 100%)、datacache_block_size(默认262144字节,即 256K)。空间不足时优先审视datacache_mem_size与datacache_disk_size是否偏小。 - 弹性扩缩容导致 Tablet 迁移:Tablet 迁移到新节点后,新节点缓存为空,存在"冷启动"阶段。
datacache.partition_duration设置不当:见 4.5 节。
4.3 Compaction 不足
若 Compaction 未能及时推进,历史数据版本长期保留,查询时需访问的 Segment 文件数量膨胀,远端 I/O 放大、查询变慢。诊断手段:
- 查看相关分区的 Compaction Score:该值应保持在10 以下。持续偏高的 Compaction Score 往往意味着 Compaction 失败或滞后。
- 查看 Query Profile 中的
SegmentsReadCount:若读取的 Segment 数量异常多,说明 Compaction 可能卡住或落后。
处置方向是推动 Compaction 恢复正常(见第七节),必要时调大 Compaction 相关线程池。
4.4 Tablet 设置不当
Tablet 负责在计算节点之间分布数据。分桶列选择不佳或桶键倾斜会导致查询只在部分节点上执行,无法充分利用集群并行能力。
建议:
- 选择能保证数据均衡分布的桶列:优先选择高基数字段,避免热点键。
- 设置合理的桶数:经验公式为
总数据量 / (1–5 GB),即每个桶承载约 1–5 GB 数据,既保证并行度,又避免桶过多带来元数据与调度开销(与 2.2 节建表缓慢的原因相互印证)。
4.5datacache.partition_duration设置不当
该属性是建表/物化视图属性datacache.partition_duration(定义于 PropertyAnalyzer.java),用于按时间维度控制哪些分区的数据允许写入缓存。
其判定逻辑见 OlapTable.java:
- 若表未按 DATE/DATETIME 分区,则所有分区都允许缓存;
- 若按 DATE/DATETIME 分区,则只有当分区结束值(end value)落在最近
datacache.partition_duration时间范围内时才允许缓存,否则禁止缓存。
因此,如果该值设置得过小,"冷"分区(时间较早的分区)数据不会被缓存,每次查询都会反复触发远端读。在 Query Profile 中若发现CompressedBytesReadRemote或IOCountRemote非零且持续存在,很可能就是此原因,应结合实际访问热区调整datacache.partition_duration。
五、Warehouse 下所有查询超时
若某个 Warehouse(计算组)下的所有查询都报Timeout was reached或Deadline Exceeded,优先检查该 Warehouse 下的 CN 节点能否访问对象存储 endpoint。
这是一个典型的环境连通性问题:shared-data 模式下每次查询都依赖 CN 与对象存储之间的网络链路,若 CN 所在网络无法访问存储 endpoint(安全组、VPC、代理、防火墙等配置问题),所有查询都会在远端 I/O 上超时。排查时可从 CN 节点上直接测试到对象存储 endpoint 的连通性(DNS 解析、TCP 连接、凭证有效性)。
六、元数据排查:如何获取 Tablet 元数据
诊断 shared-data 集群的元数据问题时,可以按以下两步获取指定 Tablet 在指定版本下的元数据 JSON:
先获取表的可见版本(Visible Version):
SHOW PARTITIONS FROM <table_name>再执行如下语句导出 Tablet 元数据:
admin execute on <backend_id> 'System.print(StorageEngine.get_lake_tablet_metadata_json(<tablet_id>, <version>))'
该命令的底层实现位于 BE 的 script.cpp:get_lake_tablet_metadata_json(tablet_id, version)通过StorageEnv::GetInstance()->lake_tablet_manager()获取 Tablet 管理器,调用get_tablet_metadata(tablet_id, version, false)读取元数据,再经proto_to_json将 protobuf 序列化为 JSON 输出。若指定版本的元数据不存在,返回对应的状态错误信息。
七、写入性能:高频导入为何变慢
shared-data 集群在高频小批量导入场景下容易变慢,其本质原因是StarRocks 的事务提交是串行化的:每个导入事务都需要依次完成提交与发布(Publish),导入频率越高,事务竞争越明显。
监控与调优时重点关注两个环节:
- Loading 队列(loading queue):若加载队列被打满,说明 I/O 处理能力不足,可增加 I/O worker 线程数。
- Publish Version 延迟:Publish 耗时高会直接拖慢后续导入,需结合 Compaction 状态与元数据服务负载综合分析。
八、Compaction:工作机制、卡死清理与跨 Warehouse 优化
8.1 shared-data 中 Compaction 如何工作,为什么会卡住?
shared-data 模式下 Compaction 的关键行为如下:
- Compaction 由 FE 调度、由 CN 执行:FE 生成压缩任务并下发,CN 上的执行器完成实际合并。
- 每次 Compaction 产生一个新版本,完整经历Write → Commit → Publish三个阶段,与普通写入事务的生命周期一致。
- FE 不跟踪运行中的 Compaction 任务:因此当 CN/BE 上存在滞留的过期任务时,FE 无法感知,这些陈旧任务可能阻塞后续新任务,导致 Compaction 卡死。
清理卡死任务的完整步骤:
对比版本信息:查看分区版本,比较
CompactVersion与VisibleVersion是否长期不一致。SHOW PARTITIONS FROM <table_name>检查 Compaction 任务状态:
SHOW PROC '/compactions'或查询 information_schema 中的云原生压缩任务表(可按 TxnID 精确过滤):
SELECT * FROM information_schema.be_cloud_native_compactions WHERE TXN_ID = <TxnID>取消过期任务:
a. 先关闭 Compaction 与迁移:
ADMIN SET FRONTEND CONFIG ("lake_compaction_max_tasks" = "0"); ADMIN SET FRONTEND CONFIG ('tablet_sched_disable_balance' = 'true'); ADMIN SHOW FRONTEND CONFIG LIKE 'lake_compaction_max_tasks'; ADMIN SHOW FRONTEND CONFIG LIKE 'tablet_sched_disable_balance';b.重启所有 BE 节点(清空内存中滞留的任务状态)。
c.确认所有 Compaction 均已失败:
SHOW PROC '/compactions'd.重新启用 Compaction 与迁移:
ADMIN SET FRONTEND CONFIG ("lake_compaction_max_tasks" = "-1"); ADMIN SET FRONTEND CONFIG ('tablet_sched_disable_balance' = 'false'); ADMIN SHOW FRONTEND CONFIG LIKE 'lake_compaction_max_tasks'; ADMIN SHOW FRONTEND CONFIG LIKE 'tablet_sched_disable_balance';其中
lake_compaction_max_tasks的默认值为-1(表示不限制任务数),定义于 Config.java。设置0相当于临时停摆调度器,重启后恢复-1即可回到默认并发水平。
8.2 为什么 Kubernetes 集群中的 Compaction 慢?
典型场景:数据写入发生在 Warehouse A,而 Compaction 却在另一个 Warehouse(例如default_warehouse)执行。此时 Compaction 需要跨 Warehouse 拉取数据,且目标节点缓存为空,等于每次合并都要走全量远端读,速度自然显著下降。
解决方案有二,可以同时采用:
- 设置 BE 配置
lake_enable_vertical_compaction_fill_data_cache = true(默认即为 true,见 config.h),让垂直 Compaction 在合并的同时回填 Data Cache,避免后续查询与合并的重复远端读; - 将写入与 Compaction 调度到同一 Warehouse,从源头消除跨 Warehouse 的数据搬运。
九、存储用量与数据生命周期
9.1 为什么存储用量看起来偏大(虚高)?
shared-data 集群中对象存储的用量包含全部历史版本(含已过期、待清理的数据),而SHOW DATA等元数据统计通常只反映最新版本,两者存在天然差异,属正常现象。
但如果差异异常巨大,请排查两类后台任务是否积压:
- Compaction 是否滞后:版本未及时合并,历史数据长期滞留(诊断方法见 8.1 节);
- Vacuum 任务是否积压:检查清理任务队列大小,若队列积压,垃圾数据无法按期清除。
必要时可调大 Compaction 与 Vacuum 的线程池,加快后台回收节奏。
9.2 为什么大查询报 "meta does not exist"?
该报错通常意味着:查询正在访问的数据版本已被 Compaction 合并并被 Vacuum 清理。即查询计划的版本过旧,对应的 Tablet 元数据已不在对象存储中。
解决办法是延长数据文件的保留期:调大 FE 配置lake_autovacuum_grace_period_minutes(自动清理的宽限期,默认30分钟,见 Config.java),让旧版本元数据在更长时间内不被回收,然后重试查询。调优时需权衡存储成本——宽限期越长,历史版本保留越久,存储占用越高。
9.3 对象存储中小文件过多怎么办?
对象存储中堆积过多小文件会带来明显性能退化。常见成因:
- 分桶设置不当导致桶数过多:每个桶被切分为大量小 Segment;
- 导入频率过高且单次导入量很小:每次导入都产生一批小文件。
Compaction 最终会把小文件合并成大文件,属于"治标"手段;更有效的预防手段是:
- 调低桶数,让每个桶承载更合理的数据量(参考 4.4 节公式);
- 批量导入,提高单次导入的数据规模,降低小文件生成频率。
十、小结:一条可复用的排查主线
综合全文,shared-data 集群排障可以归纳为一条主线:
- 先看 Profile:任何查询性能问题,先取 Query Profile,重点盯
CompressedBytesReadRemote、IOCountRemote、IOTimeRemote、SegmentsReadCount; - 再查缓存:确认
datacache_enable、datacache_mem_size、datacache_disk_size与datacache.partition_duration是否与数据访问模式匹配; - 再查 Compaction:用
SHOW PARTITIONS对比CompactVersion/VisibleVersion,用SHOW PROC '/compactions'与be_cloud_native_compactions表定位卡点,必要时按 8.1 节步骤重置; - 最后核对生命周期:结合
catalog_trash_expire_second、trash_file_expire_time_sec、lake_autovacuum_grace_period_minutes判断数据残留与"meta does not exist"类问题。
同时牢记 shared-data 的两大设计约束:事务提交串行化决定了高频小导入的瓶颈形态,FE 不跟踪运行中 Compaction决定了卡死任务的清理需要"关闭调度 + 重启 BE + 恢复调度"的组合拳。理解这些机制后,再配合本文列出的全部配置默认值(均可在 Config.java 与 config.h 中核对),你就能在真实环境中快速收敛问题。原文档位于 shared_data_faq.md,可作为日常速查手册与本文对照使用。
【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考