查分区表碎片率需逐分区计算已分配块数与实际数据字节数之差;MOVE分区须指定PARTITION关键字;PCTFREE应依更新特征设定,热分区5–10%、冷分区可设0;监控须限定活跃分区范围。
查分区表各分区的碎片率:先看真实空间浪费
oracle 不会直接告诉你“分区x碎片率72%”,得自己算——核心是对比「已分配块数」和「实际数据占满的字节数」。关键不是看整个表,而是进到每个分区里单独评估,否则大分区拖累小分区,误判风险高。
常用做法是结合 dba_tab_partitions 和 dba_segments,再用统计信息估算行平均长度:
-
SELECT语句里必须加partition_name和segment_name,避免把不同分区混在一起统计 - 依赖
ANALYZE TABLE ... COMPUTE STATISTICS或DBMS_STATS.GATHER_TABLE_STATS的结果,没更新统计信息时avg_row_len可能为 NULL 或严重失真 - 别只看
DATA_FREE(那是段级空闲空间,对分区表不直接暴露),得用blocks * 8192 - (num_rows * avg_row_len)这类估算式反推“浪费字节”
ALTER TABLE … MOVE 分区:重建前必须锁定范围
对范围分区表执行 ALTER TABLE … MOVE 默认是整表移动,但你通常只想动某个老化/膨胀严重的分区,比如 P_2024_Q1。直接 MOVE 整表会锁全表、阻塞业务,且可能让全局索引失效。
正确做法是带分区名操作:
- 语法必须写成
ALTER TABLE table_name MOVE PARTITION partition_name;,不能漏掉PARTITION关键字 - 移动后,本地索引自动维护,但全局索引需显式加
UPDATE GLOBAL INDEXES,否则会变成UNUSABLE - 如果分区有 LOB 字段,还得额外指定
LOB (lob_column) STORE AS (TABLESPACE ts_name),否则可能报 ORA-14135
PCTFREE 设定不是越大越好:得看更新模式
PCTFREE 是给“更新变长”留的缓冲,不是防碎片万能药。设太高(比如 30%),块利用率暴跌,等于主动制造碎片;设太低(比如 0),一次 UPDATE 就触发行迁移,反而加剧碎片。
判断依据很实在:
- 查
dba_tables的avg_row_len和chain_cnt:如果chain_cnt > 0,说明已有行迁移,PCTFREE很可能不够 - 看业务更新特征:字段多是
VARCHAR2(4000)但常只填 20 字节,后续又频繁 UPDATE 到 300 字节——这种就得预留空间,公式粗略是PCTFREE ≈ 100 × (300−20) / avg_block_size - 范围分区表中,不同分区生命周期不同:热分区(刚插入)可设低
PCTFREE(5–10),冷分区(只读归档)可设为 0 并MOVE彻底压实
监控脚本要带分区维度:别被 avg_row_len 带偏
很多 DBA 用统一 SQL 查全表碎片率,结果发现 WASTE_MB 很高,一查全是某个历史分区撑起来的——这不叫问题,叫事实。真正要盯的是「最近3个活跃分区」的块利用率趋势。
实操建议:
- 写监控脚本时,在
WHERE条件里强制过滤partition_name LIKE 'P\_202[56]%'这类动态表达式,避开归档分区干扰 - 别信单次
avg_row_len:对高更新表,它可能比实际更新增量小一个数量级。更稳的方式是抽样SELECT DUMP(col) FROM … WHERE ROWNUM 看真实字节分布 - 定期跑
ANALYZE TABLE table_name LIST CHAINED ROWS INTO chained_rows;,查出链化行具体在哪几个分区,比算碎片率更直击痛点
分区表的碎片从来不是“有没有”的问题,而是“哪个分区在恶化、恶化速度是否超过维护窗口”的问题。监控脚本里少一行 PARTITION 过滤,就可能把归档分区的静态浪费当成线上瓶颈来救火。










