GBase 8a 空洞率治理和历史数据清理
GBase 8a 空洞率治理和历史数据清理
我最近看 GBase 8a 这块资料时,越来越觉得很多环境里真正难处理的,不是“数据删不掉”,而是“数据看起来删了,空间却没回来,查询还越来越慢”。现场里最常见的情况就是:业务按天、按月删历史数据,表面上逻辑已经清掉了,但磁盘占用没明显下降,扫描效率也没跟着恢复。后面一看,问题往往都落在同一个点上——GBase 8a 的删除并不等于物理空间立即释放,长期累积以后,空洞率、空间占用和查询代价会一起冒出来。
我自己理解下来,GBase 8a 这类场景里,“历史数据治理”最好别只理解成一条 delete。真正落到现场时,更实际的处理顺序通常是下面这几件事:
- 先判断这张表到底适不适合直接
delete。 - 再判断应该用分区清理、
shrink space,还是重建表。 - 最后把检查、执行窗口、校验和回退一起补齐。
很多项目后面治理越来越重,不是不会清理,而是一开始把“删除数据”和“回收空间”当成了一回事。
我一般先把这类问题分成几种场景
| 现场现象 | 我优先怀疑的点 | 第一动作 |
|---|---|---|
| 删了很多历史数据,磁盘还是没降 | 逻辑删除后空间没回收 | 先看空洞率和表占用 |
| 表越跑越慢,但数据量没有继续涨很多 | 空洞率升高,扫描代价变大 | 看是否存在大量已删标记数据 |
| 时间型大表定期清理很麻烦 | 表设计没有给生命周期留出口 | 先考虑分区或天表 |
| 小表频繁更新删除,长期变碎 | 更适合定期压缩或重建 | 评估 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;
这里我自己会提醒团队两个很实在的点:
drop partition是直接删除该分区数据的,先备份再做。- 分区适合有明确边界的数据,不适合所有表都机械上。
如果业务本来就是按月、按天沉淀历史数据,那分区几乎天然适合做生命周期管理。 但如果表里的清理边界很散,硬上分区也不一定真的省事。
四、普通表已经出现空洞率问题时,我一般先评估 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;
这种方式的好处不是“更高级”,而是更容易把步骤拆清楚:
- 先生成新表。
- 再导数据。
- 再校验。
- 最后切换。
对于那些已经很臃肿、而且后续还想顺手整理结构的表,我自己更倾向把重建当成一条正式治理路线,不会只把它当成最后兜底。
六、真正落地时,我更喜欢先做一轮“候选表筛选”,再决定动谁
社区里关于表空洞率自动清理的文章,我觉得最有价值的不是某一条命令,而是它把治理思路做成了“先计算、再判断、后执行”。这个顺序特别对。因为很多表虽然有空洞,但未必值得立刻处理;也有些表空洞率不算夸张,但因为表本身特别大,可释放空间已经很可观了。
我自己更常看的几个指标
| 指标 | 我更关心什么 |
|---|---|
| 当前占用空间 | 表现在到底有多大 |
| 有效数据行数 | 不是看总行数,而是看实际还在用的数据 |
| 估算有效空间 | 真正业务数据大概占多少 |
| 可释放空间 | 做完治理大概能回多少 |
| 空洞率 | 现在碎到什么程度 |
一个简单的记录表思路
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'***' <
评论
热门帖子
- 12025-12-01浏览数:183531
- 22023-05-09浏览数:26249
- 42023-09-25浏览数:19920
- 52020-05-11浏览数:18595