某天下午我正盯着监控面板,群里突然有人发来一条消息:"BE-03磁盘使用率92%了,赶紧看看是哪张表在吃空间。"这也是我在日常运维StarRocks时最常被问的问题之一。StarRocks把数据打散存储在一组BE节点上,一份数据还有多个副本,想弄清楚某张表到底占了多大存储,远没有在MySQL里执行一条information_schema来得直接。不同口径下"大小"可以差出两三倍:是压缩前还是压缩后?算不算副本?逻辑表大小和BE磁盘上的物理占用又对不上。这篇文章我从实际运维视角,把StarRocks查看每张表占用存储的常用方法和踩坑经验完整梳理一遍,适合正在管理StarRocks集群的DBA、数据开发,以及刚接手集群想知道"底细"的朋友。
1. 为什么在StarRocks里查"表大小"和MySQL完全不是一个逻辑
先说一个最容易被忽略的背景:StarRocks并不是把一张表当成一个文件存在某台机器上。表的每个分桶对应一个tablet,tablet按照副本数(默认3)分散到不同BE节点上。一份数据实际上被拆成了很多"小格子",每个格子又有三份拷贝,错落地放在不同磁盘里。
所以"这张表占用多少存储"这句话本身就有至少三种理解角度:
- 逻辑数据量:不考虑副本、不计算索引和元数据开销,纯粹看数据内容有多大。
- 含副本的数据量:一个tablet存了三份,三份都算进去,磁盘空间确实被吃掉了三份。
- 物理磁盘占用:数据文件、索引文件、compaction中间产物、还没清理的垃圾文件,全部算进去,这才是真正让磁盘告警的那个数字。
我常用一个类比来解释:一张表像一大箱货,StarRocks会把货拆成若干小格子(tablet),每个格子复制三份分别锁进三台不同仓库。有人问"这箱货多大",你得先反问回去——你问的是单份重量、三份合计,还是连同货架和库房过道一起算的占地面积?这三个数字差异巨大,但没有一个算是错的。
搞清楚这一点后,后面所有命令和脚本就都不会看迷糊了。接下来我从最轻量、最常用的入口开始,再逐步深入到BE文件系统层面。
2. SHOW DATA:最轻量的入口,但Size口径要弄清楚
如果你只是想快速知道哪个库哪张表大,SHOW DATA是StarRocks里最直接的命令,也是我在巡检时用得最多的一个。
2.1 基本用法与输出列解读
最简单的用法是直接执行:
SHOW DATA;这会列出所有库下所有表的汇总信息。指定库和表也可以:
SHOW DATA FROM db_name; SHOW DATA FROM db_name.table_name;当指定到某张表时,输出会细到每个Index(主表以及表上的物化视图/rollup),这对定位"是不是某个物化视图把空间吃掉了"很有用。
典型的输出列大致包含这些:
| 字段 | 含义 |
|---|---|
| TableName | 表名,或者Index名 |
| Size | 数据大小,字节为单位 |
| ReplicaCount | 副本数 |
| RowCount | 行数 |
| RemoteSize | 远端存储大小(存算分离环境下出现,一般本地部署没有) |
注意,不同小版本对列的命名和展示略有差异,我看到过的输出里就有多一个RemoteSize列的情况,但核心的Size、RowCount基本都在。
2.2 Size数字里藏着两层口径:压缩和副本
这是最容易误解的地方,我单独拿出来说。
官方文档里的说法是:Size代表整个表的数据大小,展示数值为副本中数据大小的总和。结合我自己的实测,这里有两个关键点:
- **Size是压缩后的数据量。**StarRocks是列式存储,本身就带压缩,遇到重复度高的数值型字段,压缩比可以非常夸张。我见过一个线上表,导入源文件大约10GB,
SHOW DATA里Size只显示2.3GB,刚开始以为数据丢了,后来才确认是压缩效果。 - **Size默认包含了所有副本。**一张默认3副本的表,如果单份数据量是10GB,Size一般会显示30GB左右。如果这张表当时被改成了2副本,那Size就是20GB,以此类推。
所以当你想估算单份数据大小时,可以简单用Size / ReplicaCount来折算。但折算出来的也只是逻辑数据量,不是物理文件占用。
2.3 快速找出Top大表的土办法
用客户端执行SHOW DATA的结果通常是几十行甚至几百行,肉眼去扫很费劲。我一般直接在shell里处理:
mysql -h fe_host -P 9030 -u root -e "SHOW DATA FROM dwd;" \ | awk 'NR>1 {print $1, $2}' \ | sort -k2 -hr \ | head -20这里假设输出里第二列是Size字节数,NR>1跳过头两行(表头和一个分隔行)。sort -k2 -hr按数字倒序排,取前20个。这套组合拳几秒钟就能告诉我"今天又该收拾哪张表了"。
不过SHOW DATA的粒度只到表或Index,没法回答"这张表哪个分区最大""哪个BE上副本分布最不均匀"这类问题。一旦需要下钻,就要用到第三节的information_schema表。
3. information_schema.be_tablets:精确到表、分区、副本
SHOW DATA够用但不够细。真正做存储治理的时候,我更依赖information_schema.be_tablets这张系统表。它把每个BE上每个tablet的元数据都暴露出来,粒度直接到tablet,可以聚合出任意维度的存储分布。
3.1 先确认你环境里的字段
不同StarRocks版本里,be_tablets的字段有差异,尤其是列名大小写和一些新增字段。稳妥起见,先看一眼再写SQL:
DESC information_schema.be_tablets;我用的版本里常见字段有这些:
DATABASE_NAME、TABLE_NAME:库名、表名PARTITION_NAME:分区名TABLEt_ID:tablet idBE_ID:tablet副本所在BEDATA_SIZE:该副本的数据大小ROW_COUNT:行数STORAGE_PATH:tablet目录所在的存储路径INDEX_NAME:Index名
如果你执行DESC发现字段名大小写不一样,就把后面SQL里的列名相应替换一下,原理完全一样。
3.2 按表聚合:一张SQL拿到全局TopN
我最常跑的聚合查询长这样:
SELECT DATABASE_NAME, TABLE_NAME, ROUND(SUM(DATA_SIZE) / 1024 / 1024 / 1024, 2) AS size_gb, SUM(ROW_COUNT) AS row_count, COUNT(*) AS tablet_count FROM information_schema.be_tablets GROUP BY DATABASE_NAME, TABLE_NAME ORDER BY size_gb DESC LIMIT 20;这个结果和SHOW DATA的Size总体对得上,因为每个tablet在哪个BE上都会单独占一行,SUM(DATA_SIZE)天然就是把所有副本都算了进去。如果某张表是3副本,这里看到的数值大致就是单份数据量的3倍。
如果想知道单份数据量,可以按表的实际副本数折算,也可以先按DATABASE_NAME, TABLE_NAME去重后,对每个BE只取属于该表的一份副本。不过运维场景下,我更习惯直接看含副本的合计值,因为磁盘告警是由物理占用触发的,而物理占用恰恰是副本都算上的。
3.3 按分区、按BE继续下钻
这是be_tablets相比SHOW DATA最大的优势。排查"某个大分区表哪一天的数据最肥"时,直接按PARTITION_NAME聚合:
SELECT PARTITION_NAME, ROUND(SUM(DATA_SIZE) / 1024 / 1024 / 1024, 2) AS size_gb, SUM(ROW_COUNT) AS row_count, COUNT(*) AS tablet_count FROM information_schema.be_tablets WHERE DATABASE_NAME = 'dwd' AND TABLE_NAME = 'dwd_order_detail' GROUP BY PARTITION_NAME ORDER BY size_gb DESC LIMIT 20;想确认集群里BE之间是否均衡,就按BE_ID聚合:
SELECT BE_ID, ROUND(SUM(DATA_SIZE) / 1024 / 1024 / 1024, 2) AS size_gb, COUNT(*) AS tablet_count FROM information_schema.be_tablets GROUP BY BE_ID ORDER BY size_gb DESC;这两个SQL可以快速回答两个高频问题:哪张表哪个分区最占空间、哪个BE的副本总重量已经冒尖了。
有个权限上的坑需要提前提醒:be_tablets里的信息来自BE上报给FE的统计,普通低权限账号有可能查不到,或者查询返回空。我遇到过的情况是业务账号执行上面SQL直接报错。可以先跑一句SELECT COUNT(*) FROM information_schema.be_tablets;自测一下,如果没权限,找管理员给账号授权后再用。
4. 落到BE磁盘上验证物理占用
逻辑口径只能帮你"定位嫌疑表",一旦遇到磁盘真的快满、需要确认物理文件到底占了多少的情况,就得落到BE节点上看实际文件。这一步才是让告警消失的闭环。
4.1 先从BE的web端和指标接口看水位
每个BE默认会在be_http_port(通常是8040)上提供HTTP服务。浏览器访问http://BE_IP:8040,能看到该BE的基本信息入口,包括磁盘使用情况。如果集群接了Prometheus监控,在BE的metrics接口里也能直接拉到磁盘容量和实际占用相关指标。
这一步主要确认一个问题:告警的那台BE,到底是整块数据盘都满了,还是某一块盘被某个大表目录撑满了。只有确定了是哪块盘,下一步才值得去做。
4.2 tablet目录对应关系与单个大tablet的验证
BE上tablet的落盘路径大致是这样的结构:
{storage_root_path}/data/{cluster_id}/{tablet_id}/{schema_hash}tablet_id就是be_tablets里的TABLEt_ID,schema_hash是表的哈希标识。里面主要是数据文件和索引文件,例如.dat、.idx、.meta、.gc之类。
当你通过information_schema.be_tablets找到了某张表最大的几个tablet,就可以直接登录BE节点,用du验证物理目录大小:
# 在BE节点执行,查看某个大tablet的物理目录占用 du -sh /mnt/disk1/starrocks/data/10001/106234/*这里mnt/disk1/starrocks是假设的storage_root_path,10001是cluster_id,106234是tablet_id。如果结果和DATA_SIZE基本一致,说明这个tablet没有太多垃圾文件堆积;如果大出很多,多半是compaction没跟上,或存在未回收的旧版本文件。
4.3 整批统计:某个BE上每张表的物理占用
很多tablet要一个个du太慢,我建议按下面的思路做批量统计:
- 在FE上执行SQL,拉出目标BE上所有tablet_id、表名、DATA_SIZE:
SELECT TABLEt_ID, TABLE_NAME, DATABASE_NAME, DATA_SIZE FROM information_schema.be_tablets WHERE BE_ID = 10003 ORDER BY DATA_SIZE DESC;把结果保存成
tablet_list.tsv,然后登录BE节点。在BE上用一个循环脚本统计每个tablet目录的真实大小:
#!/bin/bash # 在BE节点执行 ROOT=/mnt/disk1/starrocks/storage CLUSTER_ID=$(ls ${ROOT}/data | head -1) while read tablet_id table_name; do size_bytes=$(du -sb "${ROOT}/data/${CLUSTER_ID}/${tablet_id}" 2>/dev/null | awk '{print $1}') echo -e "${tablet_id}\t${table_name}\t${size_bytes}" done < /tmp/tablet_list.tsv > /tmp/be_tablet_sizes.tsv注意,如果BE上tablet数量非常大,全量du -sb会非常慢,几十万级目录可能要跑很久。我通常只对DATA_SIZE排在前面的几百个tablet做物理验证,而不会全量做。
- 把统计结果与FE侧的SQL结果合并,就能得到"BE磁盘层面,哪张表实际吃掉了多少空间"。
4.4 为什么物理占用总是比DATA_SIZE大
这是我每次做磁盘专项排查都会遇到的问题。一个tablet的DATA_SIZE是5GB,但du出来可能有6GB甚至更多。多出来的部分主要来自:
- segment索引和元数据文件:
.idx、.meta这类文件不计入DATA_SIZE,但真实占据磁盘。 - compact过程中产生的临时文件:合并还没完成时会存在两份文件。
- 尚未被GC清理的垃圾文件:表结构变更、分区删除、副本迁移后,旧文件并不立即物理删除,而是等待垃圾回收机制处理。
- 小文件过多:tablet数量大但每个tablet数据量很小,文件系统层面Inode和最小块开销反而更明显。
所以如果你发现du的结果比SHOW DATA折算出来的大不少,先别急着认为是统计错了,绝大多数情况下是物理文件合理开销。
5. 一套可复用的排查模板:Top表、Top分区、倾斜诊断
到这里,方法论基本齐了。我把平时真正会用到的排查流程固定成了四步模板,遇到"磁盘告警"类问题直接按顺序跑一遍,基本能在十分钟内定位到根因。
5.1 四步法:从全局看到单表
第一步,看全局Top20表。用第三节的聚合SQL,或者直接SHOW DATA,确定哪些表是大头。
第二步,对Top表按分区下钻。找到最占空间的分区,看一下是不是某个近期分区异常膨胀,还是历史分区没有按预期清理。
SELECT PARTITION_NAME, ROUND(SUM(DATA_SIZE) / 1024 / 1024 / 1024, 2) AS size_gb, SUM(ROW_COUNT) AS row_count, COUNT(*) AS tablet_count FROM information_schema.be_tablets WHERE DATABASE_NAME = 'dwd' AND TABLE_NAME = 'dwd_order_detail' GROUP BY PARTITION_NAME ORDER BY size_gb DESC LIMIT 20;第三步,对嫌疑表按BE分组,看存储是否倾斜。如果某张表的tablet在BE-01上有大量副本、在BE-02上很少,说明数据分布不均衡,这在后续分桶策略调整时要重点考虑。
第四步,拉出最大tablet明细。如果一个tablet的单副本体积明显大于同表其他tablet,数值上几十倍的话,基本可以判断分桶键选择有问题,或者出现了数据倾斜:
SELECT TABLET_ID, BE_ID, ROUND(DATA_SIZE / 1024 / 1024, 2) AS size_mb, ROW_COUNT FROM information_schema.be_tablets WHERE DATABASE_NAME = 'dwd' AND TABLE_NAME = 'dwd_order_detail' ORDER BY DATA_SIZE DESC LIMIT 20;5.2 不同方法的对比,方便按场景选
我在内部文档里维护了一张简单对照表,每次写排查报告也会附上:
| 方法 | 粒度 | 是否含副本 | 是否压缩 | 精确性 | 是否需要BE权限 |
|---|---|---|---|---|---|
| SHOW DATA | 表 / Index | 含 | 压缩后逻辑大小 | 中,有汇总延迟 | 无需BE权限 |
| be_tablets聚合 | 表 / 分区 / tablet / BE | 含,每个副本一行 | 压缩后逻辑大小 | 较高,来自BE上报 | 需要information_schema权限 |
| BE文件系统du | 单个BE上的目录级 | 单节点上的副本 | 物理压缩后大小 | 最高 | 需要登录BE |
这个表解决的核心问题是"该信哪个数字"。结论是:日常巡检信SHOW DATA,下钻定位信be_tablets,磁盘到底满没满必须信du。
5.3 顺手算一下单行平均大小,也能发现隐患
用be_tablets聚合出大小和行数之后,可以顺手算一算单行平均大小:
SELECT DATABASE_NAME, TABLE_NAME, ROUND(SUM(DATA_SIZE) / SUM(ROW_COUNT), 2) AS avg_row_size, ROUND(SUM(DATA_SIZE) / 1024 / 1024 / 1024, 2) AS size_gb FROM information_schema.be_tablets WHERE ROW_COUNT > 0 GROUP BY DATABASE_NAME, TABLE_NAME ORDER BY avg_row_size DESC LIMIT 20;如果一张表行数不多,但单行平均大小异常高,多半是里面有超宽字段、长字符串,或者分桶设计不合理导致压缩率上不去。这个指标对优化表结构很有参考价值。
6. 那些年查表大小时踩过的坑
前面每一节其实都埋了一些坑,但有几个坑是反复出现的,值得集中说一说,免得你走弯路。
6.1 "数据少了"的惊吓
第一次用SHOW DATA看一张刚导入完的表时,Size比源文件小了太多,我当时第一反应是数据是不是没导全。核对行数没问题后,才反应过来是列式存储压缩的效果。数值型数据里大量重复的维度值、枚举值,压缩比可以非常夸张。后来我就养成了习惯:**拿源文件大小和表Size对比的时候,只用来估算压缩比,不要用来验证数据完整性。**验证数据完整性的靠谱方式是行数和关键字段SUM值,不是大小。
6.2 副本数改了以后,所有历史统计口径都变了
有段时间我们把一批冷表从3副本降到了2副本。降完以后SHOW DATA和be_tablets聚合出来的数值都变小了,因为副本少了一份。如果拿这个数字去跟降副本之前比,会误以为"清理出了1/3空间"。实际上逻辑数据量单份没变,只是冗余少了。所以在任何存储趋势报表里,务必记录每张表的副本数,或者干脆统一按单份口径折算,否则趋势线会被副本调整的假象带偏。
6.3 删了分区,BE磁盘空间却不掉
这可能是最让人抓狂的坑。SHOW DATA FROM已经看不到某个分区了,be_tablets里也查不到对应tablet,但df -h一看,磁盘占用纹丝不动。原因就是删除分区是标记删除,tablet数据文件要等待GC线程真正回收,而GC经常有延迟,尤其在tablet数量非常多的时候。遇到这种情况,先等一等,观察BE日志里的gc信息;如果迟迟不回收,需要检查GC是不是被卡住了。记住一个原则:逻辑上删除和物理上释放是两件事,中间隔着compaction和gc。
6.4 be_tablets的权限和版本差异
低版本StarRocks不一定有information_schema.be_tablets,就算有,字段命名在不同版本之间也可能不一致。我在2.x和3.x的环境里就遇到过DATA_SIZE和data_size写法的差异,直接照抄网上的SQL可能报错。所以每次在新环境里第一次用这个表,我都先执行DESC information_schema.be_tablets;确认一下,再写聚合SQL。另外权限问题前面也提过,普通账号很可能查不到,拿管理员账号先跑通,再决定怎么给业务账号授权。
6.5 不要依赖客户端格式化输出做脚本自动化
如果你们是直接执行SHOW DATA然后接awk处理,有个细节要留神:某些管理平台或客户端会把Size显示成"2.3 GB"这种人类可读格式,而不是纯数字字节。这时候sort -hr还能勉强用,但如果你做的是精确汇总,建议统一走be_tablets按字节算,不要在格式化字符串上做运算。我在写自动化巡检脚本时踩过这个坑,后来所有统计一律以information_schema.be_tablets为准,SHOW DATA只用来人工肉眼巡检。
我个人现在的日常习惯是:每日巡检用SHOW DATA看有没有表的Size突然异常增长;出了磁盘告警,立刻用be_tablets做表、分区、BE三轮下钻;如果还定位不到,直接上BE节点对前几百个大tablet目录做du验证。这套流程基本覆盖了我遇到过的所有"表把磁盘吃满了"的场面。最后再分享一个小经验:把这些SQL固化成一个巡检脚本,每周跑一次,把Top表和Top分区的变化趋势记下来,很多存储问题在爆发前其实是有苗头的。