GBase 8a 空洞率治理和历史数据清理我最近看 GBase 8a 这块资料时越来越觉得很多环境里真正难处理的不是“数据删不掉”而是“数据看起来删了空间却没回来查询还越来越慢”。现场里最常见的情况就是业务按天、按月删历史数据表面上逻辑已经清掉了但磁盘占用没明显下降扫描效率也没跟着恢复。后面一看问题往往都落在同一个点上——GBase 8a 的删除并不等于物理空间立即释放长期累积以后空洞率、空间占用和查询代价会一起冒出来。我自己理解下来GBase 8a 这类场景里“历史数据治理”最好别只理解成一条delete。真正落到现场时更实际的处理顺序通常是下面这几件事先判断这张表到底适不适合直接delete。再判断应该用分区清理、shrink space还是重建表。最后把检查、执行窗口、校验和回退一起补齐。很多项目后面治理越来越重不是不会清理而是一开始把“删除数据”和“回收空间”当成了一回事。我一般先把这类问题分成几种场景现场现象我优先怀疑的点第一动作删了很多历史数据磁盘还是没降逻辑删除后空间没回收先看空洞率和表占用表越跑越慢但数据量没有继续涨很多空洞率升高扫描代价变大看是否存在大量已删标记数据时间型大表定期清理很麻烦表设计没有给生命周期留出口先考虑分区或天表小表频繁更新删除长期变碎更适合定期压缩或重建评估shrink space full窗口想马上回空间但业务不能长时间停清理方式选错了比较分区删除、shrink和重建代价我最近整理下来觉得GBase 8a 的生命周期治理最怕的不是“没有手段”而是不同表用同一种办法硬套。大表、小表时间边界明确和边界不明确代价差别非常大。一、先把“删除数据”和“回收空间”分开理解这个点我现在会先跟团队说清楚。GBase 8a 删除数据以后并不会立刻把磁盘空间物理释放掉而是先把对应数据标记成已删除状态。这样做的好处是删除动作本身更直接但后面就会带来两个常见问题磁盘空间没有按预期回来。已删除数据比例高了以后表扫描和查询会更浪费资源。这也是为什么很多环境里业务已经持续做历史清理了但表还是越来越“胖”。这个现象我通常怎么判断我现场里一般不会先做清理而是先拉一轮表级信息确认这张表是不是已经值得动了select*fromperformance_schema.tableswhereTABLE_SCHEMAodsandTABLE_NAMEorder_hist;如果是已经知道表特别大、而且近期确实删过很多数据我还会把“业务上删掉了多少”和“物理空间有没有明显回落”放在一起看。这个动作很简单但很关键因为有些表看起来删了很多实际可释放空间并不大有些表则反过来删掉的数据占比已经不低但还没人处理。二、别一上来就shrink space先看这张表属于哪一类GBase 8a 这块我后来越来越认同一个思路先分表类型再选回收方式。我自己更常用的分类办法表类型我更倾向的处理方式原因明显按时间增长的大表优先考虑天表或分区表生命周期边界明确清理成本最低已经建好的普通大表评估shrink space/shrink space full/ 重建表看窗口、可释放空间和阻塞代价小表但频繁更新删除定期shrink space full或重建表小治理窗口更容易拿需要快速整月、整天清理的数据分区删除或分区截断比逐行delete更直接长期没有分区、数据量特别大不急着直接压缩先算代价很多时候先补生命周期设计更值我个人比较在意一个点如果数据本身有天然时间边界最优解通常不是 delete 之后再补救而是从表结构层面就给清理动作留出口。三、时间边界清楚的大表我更倾向优先做分区治理社区里关于 GBase 8a 分区表的资料我自己觉得有个点特别实用分区不只是为了查询裁剪也非常适合做生命周期管理。因为对有明显时间边界的数据来说“整段清理”本来就比“逐行删除”更自然。一个比较常见的 RANGE 分区示意createtableods.order_hist(order_idbigint,cust_idbigint,statis_datedate,order_amtdecimal(18,2))partitionbyrange(year(statis_date)*100month(statis_date))(partitionp202401valuesless than(202402),partitionp202402valuesless than(202403),partitionp202403valuesless than(202404),partitionpmaxvaluesless than maxvalue);这种设计的好处很实际数据按月份分段生命周期边界清楚。查询按时间条件时更容易缩小扫描范围。清理时可以按分区动作做不用靠大批量delete。分区治理里我更常用的几个动作-- 增加新分区altertableods.order_histaddpartition(partitionp202404valuesless than(202405));-- 删除旧分区altertableods.order_histdroppartitionp202401;-- 截断指定分区altertableods.order_histtruncatepartitionp202402;这里我自己会提醒团队两个很实在的点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_ratioN在释放率和耗时之间做折中想比普通 shrink 更彻底但又不想全量极限整理重建表最可控也最像重新整理一次能接受重建成本且想把格式和结构一起理顺一个常见的回收示意altertableods.order_hist shrink space;altertableods.order_hist shrink spacefull;altertableods.order_hist shrink spacefullblock_reuse_ratio30;我自己更常这样选场景我更倾向的方式大表、短窗口、先回一部分空间shrink space小表、频繁更新删除、想定期整理shrink space full大表但可释放空间不少且想折中shrink space full block_reuse_ratio业务能接受重建重建表这个地方我个人最想强调的是shrink space不是“想什么时候跑就什么时候跑”的轻操作。社区资料里提到这类动作本质上是 DDL执行期间会对表访问产生影响。采用 rebalance 方式时可以放松部分限制但也不是完全无影响。所以真正落到生产窗口期一定要先拿到。五、重建表这件事很多时候比硬压缩更稳有些现场里一提空间回收大家第一反应就是shrink。但我自己做下来反而觉得重建表有时候更容易把风险讲清楚。尤其是下面几类情况表已经很碎业务也能接受一次集中窗口。想顺手把存储属性、分布方式或者字段顺序也一起整理。当前版本和现场条件下不想把空间整理全部压在单个shrink动作上。我更常用的一种重建思路createtableods.order_hist_newlikeods.order_hist;insertintoods.order_hist_newselect*fromods.order_histwherestatis_date2024-01-01;renametableods.order_histtoods.order_hist_bak;renametableods.order_hist_newtoods.order_hist;再配合业务窗口和抽样校验把原表保留一段时间后再删droptableods.order_hist_bak;这种方式的好处不是“更高级”而是更容易把步骤拆清楚先生成新表。再导数据。再校验。最后切换。对于那些已经很臃肿、而且后续还想顺手整理结构的表我自己更倾向把重建当成一条正式治理路线不会只把它当成最后兜底。六、真正落地时我更喜欢先做一轮“候选表筛选”再决定动谁社区里关于表空洞率自动清理的文章我觉得最有价值的不是某一条命令而是它把治理思路做成了“先计算、再判断、后执行”。这个顺序特别对。因为很多表虽然有空洞但未必值得立刻处理也有些表空洞率不算夸张但因为表本身特别大可释放空间已经很可观了。我自己更常看的几个指标指标我更关心什么当前占用空间表现在到底有多大有效数据行数不是看总行数而是看实际还在用的数据估算有效空间真正业务数据大概占多少可释放空间做完治理大概能回多少空洞率现在碎到什么程度一个简单的记录表思路createtableifnotexistsops.table_space_check(dbnamevarchar(128),tbnamevarchar(128),current_disk_sizebigint,record_countbigint,calc_disk_sizebigint,can_release_disk_sizebigint,hole_ratiodecimal(10,4),check_timedatetime);配合脚本定时巡检把候选表先筛出来再人工确认是否执行。我自己很喜欢这种思路因为它把空间治理从“出了问题才临时手工处理”变成了“可以持续运营的一件事”。七、执行窗口这件事别等到业务高峰再想起来GBase 8a 这类表空间整理真正现场里最容易忽略的不是命令而是时机。空洞率治理如果做得太临时常常会出现两个问题业务窗口没拿到执行一半又得停。同时撞上数据同步、导入、报表高峰系统又被额外拉高。所以我现在更倾向于把空间治理和跑批窗口一起看而不是把它当成 DBA 临时单独干的一件事。一个比较常见的 Shell 调度思路#!/bin/bashset-egccli-uroot-p***EOF alter table ods.order_hist shrink space full block_reuse_ratio30; EOF配合crontab做成固定窗口比如晚上先扫描候选表再在更晚的时间段执行一到两张表。这个动作看起来不复杂但我自己觉得很实用因为真正现场里最怕的不是不会清而是一次性清太多表把系统又拖出新的波动。八、我自己比较容易提醒团队的几个坑1把delete当成生命周期治理的终点这几乎是最常见的误区。delete只是业务逻辑上把数据清了后面空间是不是回来、扫描是不是变轻还得看后续治理动作。2所有大表都想靠shrink space full这个思路太理想化了。有些大表更适合分区有些更适合重建有些则只能在特别明确的窗口里做整理。没有必要所有表都走一条路。3不先看边界就上分区分区特别适合有时间边界的数据但并不是所有表都值得改造成分区表。边界散、写入模式复杂的表硬改未必省事。4只看空洞率不看可释放空间有的表空洞率高但表本身不大有的表空洞率一般但因为总量太大可释放空间已经很可观。只盯一个比例容易判断失真。5治理动作和业务窗口脱节空间治理是运维动作但它对业务是有影响的。这个关系不先讲透最后通常不是动作做不成就是做完了业务侧反而很被动。九、我现在更认可的一套 GBase 8a 生命周期治理顺序如果让我现在给一个 GBase 8a 环境做历史数据治理我通常会按这个顺序来先分表类型看它有没有明确时间边界是大表、小表还是频繁更新删除表。再选治理路线分区删除、shrink space、shrink space full、重建表不要混着上。然后做候选表筛选先算空洞率、可释放空间和执行代价再决定动谁。再拿执行窗口别跟同步、导入、报表高峰撞在一起。最后补校验和回退清理前备份边界清理后看空间、行数和业务抽样。结尾收一下我最近重新整理 GBase 8a 这块内容时越来越觉得“历史数据清理”这件事真正难的从来不是语法而是顺序。很多环境里空间紧张、表越来越胖、查询越来越慢表面上看像是存储问题实际往往是生命周期策略没提前设计好。从落地角度看我个人更倾向于这么理解delete 解决的是逻辑清理不等于物理回收有时间边界的大表优先考虑分区或天表普通表的空间治理要在shrink、full shrink和重建之间做取舍真正做得稳的不是最会执行命令的环境而是能持续筛表、持续治理、持续校验的环境。参考资料[1] Gbase 运维-表空洞率自动清理 https://www.gbase.cn/community/post/3428 [2] 空洞率学习分享 https://www.gbase.cn/community/post/6965 [3] GBase 8a集群删除数据后释放磁盘空间shrink space https://www.gbase.cn/community/post/341 [4] shrink space各模式及推荐使用 https://www.gbase.cn/community/post/5388 [5] Gbase8a分区表的使用 https://www.gbase.cn/community/post/4603 [6] gbase常用分区表的各种操作 https://www.gbase.cn/community/post/3332 [7] 数据加载 https://www.gbase.cn/docs/gbase-8a/%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8C/dm-database-management-guide/dm-data-integration-manage/dm-data-loading