Oracle数据库如何清理表碎片

2026年01月21日/ 浏览 5

表碎片主要是由于频繁的DML操作导致数据块中出现空闲空间,使得高水位线(HWM)下存在大量无数据块。数据库中的表碎片会降低查询效率,增加不必要的I/O操作。以下是收缩表碎片的常用方法,以 Oracle 数据库为例。

使用 SHRINK SPACE命令(Oracle 10g 及以上)

这是Oracle官方推荐的在线碎片整理方法,对业务影响较小。

启用行移动:这是执行收缩操作的前提。ALTER TABLE your_table_name ENABLE ROW MOVEMENT;执行收缩:根据你的需求选择不同的收缩选项。

操作

说明

适用场景

ALTER TABLE your_table_name SHRINK SPACE COMPACT;

仅重组数据,不降低高水位线(HWM),不释放空间。对业务影响小。

业务繁忙时段进行初步整理。

ALTER TABLE your_table_name SHRINK SPACE;

重组数据并降低高水位线,释放空间。此过程会短暂锁表。

业务低峰期进行最终的空间回收。

ALTER TABLE your_table_name SHRINK SPACE CASCADE;

收缩表的同时,自动收缩该表上的所有索引。

需要同时整理表和索引碎片。

关闭行移动(可选):收缩完成后,可以选择关闭行移动。

ALTER TABLE your_table_name DISABLE ROW MOVEMENT;

⚙️ 使用 MOVE命令

MOVE表是另一种整理碎片的方法,但它在操作期间会对表施加更强的锁,影响DML操作,并且会使所有索引失效。

ALTER TABLE your_table_name MOVE;

操作完成后,必须重建索引

ALTER INDEX your_index_name REBUILD;

操作前的重要检查

确认碎片程度:在操作前,先查询数据字典判断碎片化的严重程度。

SELECT table_name, ROUND((blocks * 8), 2) "高水位空间 k", ROUND((num_rows * avg_row_len / 1024), 2) "真实使用空间 k", ROUND((blocks * 8) - (num_rows * avg_row_len / 1024) - (blocks * 8 * 10 / 100), 2) "浪费空间 k" FROM user_tables WHERE table_name = YOUR_TABLE_NAME;

如果“浪费空间”很大,则整理的必要性很高。

确认表空间管理方式:SHRINK命令要求表所在的表空间为自动段空间管理(ASSM)

注意限制:SHRINK命令不适用于簇表、包含LONG类型的表、带有函数索引的表以及压缩表等。

评估空间回收效果

通过对比整理前后表所占用的空间和碎片率来判断效果。

查看段空间使用情况:查询 DBA_SEGMENTS视图,关注 BYTES和 BLOCKS列。整理成功后,这些值应该有明显下降。

SELECT segment_name, bytes/1024/1024 as "SIZE_MB", blocks FROM dba_segments WHERE segment_name = YOUR_TABLE_NAME AND owner = TABLE_OWNER;

计算表碎片率:通过 USER_TABLES或 DBA_TABLES中的统计信息计算,可以量化碎片程度。整理后,这个比率应该趋近于0。

SELECT table_name, ROUND((blocks * 8192 / 1024 / 1024), 2) AS "HWM_SIZE_MB", ROUND((num_rows * avg_row_len / 1024 / 1024), 2) AS "ACTUAL_DATA_SIZE_MB", ROUND((blocks * 8192 / 1024 / 1024) - (num_rows * avg_row_len / 1024 / 1024), 2) AS "FRAGMENTED_SPACE_MB", CASE WHEN blocks > 0 THEN ROUND((num_rows * avg_row_len) / (blocks * 8192), 4) * 100 ELSE 0 END AS "EFFECTIVENESS_PERCENTAGE" FROM dba_tables WHERE table_name = YOUR_TABLE_NAME AND owner = TABLE_OWNER;

碎片空间(FRAGMENTED_SPACE_MB):理想情况下应大幅减少。

表使用效率(EFFECTIVENESS_PERCENTAGE):这个百分比越高,说明HWM以下的数据块填充越满,效果越好。

注意:在运行这些查询前,请先收集最新的表统计信息,以确保数据准确。

EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME => TABLE_OWNER, TABNAME => YOUR_TABLE_NAME);

⚡ 分析性能改善

碎片整理的一个重要目标是减少全表扫描时需要读取的数据块数量,从而提升查询速度。

比较执行计划成本:对表进行简单的全表扫描查询,使用 EXPLAIN PLAN查看执行计划。整理后,全表扫描的 Cost(成本) 值应该有所下降。EXPLAIN PLAN FOR SELECT * FROM your_table_name; SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

检查索引与对象状态

如果使用了 ALTER TABLE ... MOVE或 SHRINK SPACE CASCADE等方法,需要确保表和索引仍然处于有效状态。

检查索引状态:查询 DBA_INDEXES视图。SELECT index_name, status FROM dba_indexes WHERE table_name = YOUR_TABLE_NAME AND table_owner = TABLE_OWNER;如果状态不是 VALID,则需要重建索引。ALTER INDEX your_index_name REBUILD ONLINE;

进行深度空间分析

对于特别重要的表,可以使用Oracle提供的 DBMS_SPACE包进行更深入的空间分析,查看数据块内部的利用率。

DECLARE l_unformatted_blocks NUMBER; l_unformatted_bytes NUMBER; l_fs1_blocks NUMBER; l_fs1_bytes NUMBER; l_fs2_blocks NUMBER; l_fs2_bytes NUMBER; l_fs3_blocks NUMBER; l_fs3_bytes NUMBER; l_fs4_blocks NUMBER; l_fs4_bytes NUMBER; l_full_blocks NUMBER; l_full_bytes NUMBER; BEGIN DBMS_SPACE.SPACE_USAGE( segment_owner => TABLE_OWNER, segment_name => YOUR_TABLE_NAME, segment_type => TABLE, unformatted_blocks => l_unformatted_blocks, unformatted_bytes => l_unformatted_bytes, fs1_blocks => l_fs1_blocks, fs1_bytes => l_fs1_bytes, fs2_blocks => l_fs2_blocks, fs2_bytes => l_fs2_bytes, fs3_blocks => l_fs3_blocks, fs3_bytes => l_fs3_bytes, fs4_blocks => l_fs4_blocks, fs4_bytes => l_fs4_bytes, full_blocks => l_full_blocks, full_bytes => l_full_bytes ); DBMS_OUTPUT.PUT_LINE(Full Blocks (75-100% full): || l_full_blocks); DBMS_OUTPUT.PUT_LINE(FS4 Blocks (50-75% full): || l_fs4_blocks); -- ... 可以输出其他分段的数据 END; /

整理成功后,full_blocks(利用率75%-100%的块)数量应增加,而 fs1_blocks(0-25%满)等利用率低的块应减少。

操作建议

在业务低峰期操作:尤其是执行会降低HWM的阶段(如 SHRINK SPACE的第二阶段或 MOVE操作),可能会短时间锁表,建议在业务低峰期进行。更新统计信息:碎片整理完成后,务必重新收集表的统计信息,以便基于最新数据状态生成最优的执行计划。整体维护:表碎片整理是数据库常规维护的一部分。对于频繁进行增删改操作(DML)的表,需要定期检查和整理。

picture loss