GBase 8a
运维管理
文章
精选

GBase 8a 空洞率治理和历史数据清理

发表于2026-04-02 09:26:00202次浏览4个评论

GBase 8a 空洞率治理和历史数据清理

我最近看 GBase 8a 这块资料时,越来越觉得很多环境里真正难处理的,不是“数据删不掉”,而是“数据看起来删了,空间却没回来,查询还越来越慢”。现场里最常见的情况就是:业务按天、按月删历史数据,表面上逻辑已经清掉了,但磁盘占用没明显下降,扫描效率也没跟着恢复。后面一看,问题往往都落在同一个点上——GBase 8a 的删除并不等于物理空间立即释放,长期累积以后,空洞率、空间占用和查询代价会一起冒出来。

我自己理解下来,GBase 8a 这类场景里,“历史数据治理”最好别只理解成一条 delete。真正落到现场时,更实际的处理顺序通常是下面这几件事:

  1. 先判断这张表到底适不适合直接 delete。
  2. 再判断应该用分区清理、shrink space,还是重建表。
  3. 最后把检查、执行窗口、校验和回退一起补齐。

很多项目后面治理越来越重,不是不会清理,而是一开始把“删除数据”和“回收空间”当成了一回事。

我一般先把这类问题分成几种场景

现场现象 我优先怀疑的点 第一动作
删了很多历史数据,磁盘还是没降 逻辑删除后空间没回收 先看空洞率和表占用
表越跑越慢,但数据量没有继续涨很多 空洞率升高,扫描代价变大 看是否存在大量已删标记数据
时间型大表定期清理很麻烦 表设计没有给生命周期留出口 先考虑分区或天表
小表频繁更新删除,长期变碎 更适合定期压缩或重建 评估 shrink space full 窗口
想马上回空间,但业务不能长时间停 清理方式选错了 比较分区删除、shrink 和重建代价

我最近整理下来觉得,GBase 8a 的生命周期治理,最怕的不是“没有手段”,而是不同表用同一种办法硬套。大表、小表,时间边界明确和边界不明确,代价差别非常大。

一、先把“删除数据”和“回收空间”分开理解

这个点我现在会先跟团队说清楚。 GBase 8a 删除数据以后,并不会立刻把磁盘空间物理释放掉,而是先把对应数据标记成已删除状态。这样做的好处是删除动作本身更直接,但后面就会带来两个常见问题:

  • 磁盘空间没有按预期回来。
  • 已删除数据比例高了以后,表扫描和查询会更浪费资源。

这也是为什么很多环境里,业务已经持续做历史清理了,但表还是越来越“胖”。

这个现象我通常怎么判断

我现场里一般不会先做清理,而是先拉一轮表级信息,确认这张表是不是已经值得动了:

select *
from performance_schema.tables
where TABLE_SCHEMA = 'ods'
  and TABLE_NAME = 'order_hist';

如果是已经知道表特别大、而且近期确实删过很多数据,我还会把“业务上删掉了多少”和“物理空间有没有明显回落”放在一起看。这个动作很简单,但很关键,因为有些表看起来删了很多,实际可释放空间并不大;有些表则反过来,删掉的数据占比已经不低,但还没人处理。

二、别一上来就 shrink space,先看这张表属于哪一类

GBase 8a 这块我后来越来越认同一个思路: 先分表类型,再选回收方式。

我自己更常用的分类办法

表类型 我更倾向的处理方式 原因
明显按时间增长的大表 优先考虑天表或分区表 生命周期边界明确,清理成本最低
已经建好的普通大表 评估 shrink space / shrink space full / 重建表 看窗口、可释放空间和阻塞代价
小表但频繁更新删除 定期 shrink space full 或重建 表小,治理窗口更容易拿
需要快速整月、整天清理的数据 分区删除或分区截断 比逐行 delete 更直接
长期没有分区、数据量特别大 不急着直接压缩,先算代价 很多时候先补生命周期设计更值

我个人比较在意一个点: 如果数据本身有天然时间边界,最优解通常不是 delete 之后再补救,而是从表结构层面就给清理动作留出口。

三、时间边界清楚的大表,我更倾向优先做分区治理

社区里关于 GBase 8a 分区表的资料,我自己觉得有个点特别实用: 分区不只是为了查询裁剪,也非常适合做生命周期管理。因为对有明显时间边界的数据来说,“整段清理”本来就比“逐行删除”更自然。

一个比较常见的 RANGE 分区示意

create table ods.order_hist (
  order_id        bigint,
  cust_id         bigint,
  statis_date     date,
  order_amt       decimal(18,2)
)
partition by range (year(statis_date) * 100 + month(statis_date)) (
  partition p202401 values less than (202402),
  partition p202402 values less than (202403),
  partition p202403 values less than (202404),
  partition pmax values less than maxvalue
);

这种设计的好处很实际:

  • 数据按月份分段,生命周期边界清楚。
  • 查询按时间条件时,更容易缩小扫描范围。
  • 清理时可以按分区动作做,不用靠大批量 delete。

分区治理里我更常用的几个动作

-- 增加新分区
alter table ods.order_hist
add partition (partition p202404 values less than (202405));

-- 删除旧分区
alter table ods.order_hist
drop partition p202401;

-- 截断指定分区
alter table ods.order_hist
truncate partition p202402;

这里我自己会提醒团队两个很实在的点:

  1. drop partition 是直接删除该分区数据的,先备份再做。
  2. 分区适合有明确边界的数据,不适合所有表都机械上。

如果业务本来就是按月、按天沉淀历史数据,那分区几乎天然适合做生命周期管理。 但如果表里的清理边界很散,硬上分区也不一定真的省事。

四、普通表已经出现空洞率问题时,我一般先评估 shrink space 还是 shrink space full

这块也是 GBase 8a 现场里最容易被直接上命令的地方。 我自己现在不会一看到空间没回来就立刻跑 alter table ... shrink space,因为不同模式的代价和效果差别挺大。

社区里关于 shrink space 的资料已经把几个模式差异讲得比较清楚了。按我目前整理下来的理解,大致可以这样看:

方式 我更倾向怎么理解 适用场景
shrink space 回收较快,但空间释放不一定彻底 业务窗口短、先想止血
shrink space full 更彻底,更像深度整理 表不算太大,能拿到窗口
shrink space full block_reuse_ratio=N 在释放率和耗时之间做折中 想比普通 shrink 更彻底,但又不想全量极限整理
重建表 最可控,也最像重新整理一次 能接受重建成本,且想把格式和结构一起理顺

一个常见的回收示意

alter table ods.order_hist shrink space;

alter table ods.order_hist shrink space full;

alter table ods.order_hist shrink space full block_reuse_ratio=30;

我自己更常这样选

场景 我更倾向的方式
大表、短窗口、先回一部分空间 shrink space
小表、频繁更新删除、想定期整理 shrink space full
大表但可释放空间不少,且想折中 shrink space full block_reuse_ratio
业务能接受重建 重建表

这个地方我个人最想强调的是: shrink space 不是“想什么时候跑就什么时候跑”的轻操作。 社区资料里提到,这类动作本质上是 DDL,执行期间会对表访问产生影响。采用 rebalance 方式时,可以放松部分限制,但也不是完全无影响。所以真正落到生产,窗口期一定要先拿到。

五、重建表这件事,很多时候比硬压缩更稳

有些现场里,一提空间回收,大家第一反应就是 shrink。但我自己做下来,反而觉得重建表有时候更容易把风险讲清楚。尤其是下面几类情况:

  • 表已经很碎,业务也能接受一次集中窗口。
  • 想顺手把存储属性、分布方式或者字段顺序也一起整理。
  • 当前版本和现场条件下,不想把空间整理全部压在单个 shrink 动作上。

我更常用的一种重建思路

create table ods.order_hist_new like ods.order_hist;

insert into ods.order_hist_new
select *
from ods.order_hist
where statis_date >= '2024-01-01';

rename table ods.order_hist to ods.order_hist_bak;
rename table ods.order_hist_new to ods.order_hist;

再配合业务窗口和抽样校验,把原表保留一段时间后再删:

drop table ods.order_hist_bak;

这种方式的好处不是“更高级”,而是更容易把步骤拆清楚:

  1. 先生成新表。
  2. 再导数据。
  3. 再校验。
  4. 最后切换。

对于那些已经很臃肿、而且后续还想顺手整理结构的表,我自己更倾向把重建当成一条正式治理路线,不会只把它当成最后兜底。

六、真正落地时,我更喜欢先做一轮“候选表筛选”,再决定动谁

社区里关于表空洞率自动清理的文章,我觉得最有价值的不是某一条命令,而是它把治理思路做成了“先计算、再判断、后执行”。这个顺序特别对。因为很多表虽然有空洞,但未必值得立刻处理;也有些表空洞率不算夸张,但因为表本身特别大,可释放空间已经很可观了。

我自己更常看的几个指标

指标 我更关心什么
当前占用空间 表现在到底有多大
有效数据行数 不是看总行数,而是看实际还在用的数据
估算有效空间 真正业务数据大概占多少
可释放空间 做完治理大概能回多少
空洞率 现在碎到什么程度

一个简单的记录表思路

create table if not exists ops.table_space_check (
  dbname                varchar(128),
  tbname                varchar(128),
  current_disk_size     bigint,
  record_count          bigint,
  calc_disk_size        bigint,
  can_release_disk_size bigint,
  hole_ratio            decimal(10,4),
  check_time            datetime
);

配合脚本定时巡检,把候选表先筛出来,再人工确认是否执行。 我自己很喜欢这种思路,因为它把空间治理从“出了问题才临时手工处理”,变成了“可以持续运营的一件事”。

七、执行窗口这件事,别等到业务高峰再想起来

GBase 8a 这类表空间整理,真正现场里最容易忽略的不是命令,而是时机。 空洞率治理如果做得太临时,常常会出现两个问题:

  • 业务窗口没拿到,执行一半又得停。
  • 同时撞上数据同步、导入、报表高峰,系统又被额外拉高。

所以我现在更倾向于把空间治理和跑批窗口一起看,而不是把它当成 DBA 临时单独干的一件事。

一个比较常见的 Shell 调度思路

#!/bin/bash
set -e

gccli -uroot -p'***' <

评论

登录后才可以发表评论
用户头像
山佳发表于 5个月前
111
用户头像
柒柒天晴发表于 5个月前
111
GBase用户47954发表于 5个月前
感谢作者的精彩分享!
流泪猫猫头发表于 3个月前
很详细的文章