当前位置: 代码网 > it编程>数据库>Oracle > Oracle表碎片整理方法(alter table move)操作总结

Oracle表碎片整理方法(alter table move)操作总结

2026年09月22日 Oracle 我要评论
一、表碎片的产生原因与影响oracle 表在经历大量 insert、delete、update 操作后,数据块中会产生大量"空洞"。这些空洞是被删除数据腾出来的空间,但它们没有被有

一、表碎片的产生原因与影响

oracle 表在经历大量 insertdeleteupdate 操作后,数据块中会产生大量"空洞"。这些空洞是被删除数据腾出来的空间,但它们没有被有效回收,依然被表的**高水位线(high water mark, hwm)**标记为"已使用"。

碎片带来的危害:

  • 存储浪费:已分配但无法有效利用的空间
  • 性能下降:全表扫描需要读取更多数据块,i/o开销增大
  • 索引效率降低:索引与数据块之间的映射关系变得松散
  • 高水位线问题:即使表内数据很少,全表扫描仍会扫描到高水位线以下的所有块

碎片检测方法:

-- 收集表的统计信息
begin

  dbms_stats.gather_table_stats(ownname=>'用户名',
                                tabname=>'表名',
                                estimate_percent=>1,
                                method_opt=>'for all columns size 1',
                                no_invalidate=>false,
                                cascade=>true,
                                degree => 4); 
end; 
/

-- 对比表的实际数据大小与段占用大小
set linesize 200 pagesize 999
col owner format a15
col table_name format a20
col column_name format a12
select owner,
       table_name,
       num_rows,
       avg_row_len,
       blocks,
       round(blocks * 8 / 1024, 2) as "hwm_mb",
       round(num_rows * avg_row_len / 1024 / 1024, 2) as "real_data_mb",
       round((blocks * 8 - num_rows * avg_row_len / 1024) / 1024, 2) as "waste_mb"
from dba_tables
where owner = '用户名'
  and table_name = '表名';
若hwm_mb远大于 real_data_mb(如 500mb vs 20mb),则说明碎片严重,需要整理。
  
-- 评估数据库中大表碎片率
set linesize 200 pagesize 999
col owner format a15
col table_name format a20
col column_name format a12
select owner,
    table_name,
    num_rows,
    blocks,
    avg_row_len,
    round((1 - (num_rows * avg_row_len / (blocks * 8192))) * 100, 2) as fragment_pct
from dba_tables
where blocks > 100  -- 只关注大表
order by fragment_pct desc;

二、表碎片整理的四种方法

方法核心原理是否在线索引影响依赖对象和权限等额外空间需求
create table as select (ctas)将有效数据创建为新表,再重命名替换否(需停业务)需手动重建需要手工处理,复杂需要一倍于表的空间
alter table move将表数据物理搬迁到新的段中,重建整个表否(全程排他锁)全部失效,需重建不受影响需要一倍于表的空间
dbms_redefinition(在线重定义)通过物化视图日志同步数据,实现零停机重组是(近乎零停机)自动维护通过自带过程一键完成需要额外空间
alter table shrink space原地整理数据块,将数据行向前压缩是(大部分过程在线)自动维护,保持有效不受影响几乎不需要额外空间

本文描述alter table move方法,其它三种方法请参考文章《oracle表在线重定义操作总结(dbms_redefinition)》《oracle ctas+rename 表重建方法操作总结》《oracle表碎片整理方法(alter table shrink)操作总结》

三、alter table move 操作

1、原理与特点

alter table ... move通过完全重新组织表的段(segment),将数据按顺序重新写入新的数据块,完成后,删除旧段,将新段重命名为原表,彻底消除碎片并重置高水位线。

由于数据被物理搬迁到全新的数据块中,每一行的 rowid 都会发生变化。这意味着所有基于 rowid 的索引全部失效(状态变为 unusable),必须手动重建。

move优点:

  • 碎片清理最彻底,hwm 完全重置到最低位置
  • 可以顺便调整存储参数(pctfree、pctused、表空间迁移等)
  • 全表扫描性能恢复到最优水平
  • oracle 12c 及以上支持 move online(仅限堆表),可减少锁表时间
  • 表上的依赖对象(触发器、包、函数、视图)和权限,不受影响,不需任何改动!

move缺点(代价):

  • 全程排他锁,操作期间表不可进行任何 dml 操作
  • 需要额外一倍于表的空闲空间(新旧段同时存在)
  • 所有索引全部失效,必须手动重建
  • 操作期间产生大量 i/o 和 redo(除非使用 nologging)

2、move操作语法

-- 基本用法:原地 move(同表空间)
alter table your_table move;

-- 指定新表空间
alter table your_table move tablespace new_tablespace;

-- 同时调整 pctfree(可以顺便优化存储参数)
alter table your_table move pctfree 10 pctused 40;

-- 重建所有索引
select 'alter index ' || index_name || ' rebuild nologging parallel 4;' 
from dba_indexes 
where owner = '用户名' and table_name = '表名';

-- 执行生成的sql后,关闭并行
select 'alter index ' || index_name || ' logging noparallel;' 
from dba_indexes 
where owner = '用户名'  table_name = '表名';

3、并发(parallel)+ nologging

开启并行 + nologging(大表推荐,减少 redo 生成)

-- 使用并行度4进行move(利用多核cpu)、nologging(减少redo生成)
alter table your_table move nologging parallel 4;

--制定新表空间
alter table your_table move nologging parallel 4 tablespace new_tablespace;

-- 操作完成后应立即关闭并行
alter table your_table noparallel logging;

-- 重建所有索引
select 'alter index ' || index_name || ' rebuild nologging parallel 4;' 
from dba_indexes 
where owner = '用户名' and table_name = '表名' and index_type <>'lob';

-- 执行生成的sql后,关闭并行
select 'alter index ' || index_name || ' logging noparallel;' 
from dba_indexes 
where owner = '用户名'  table_name = '表名' and index_type <>'lob';

4、表中lob字段的move策略

包含lob字段的表,move时必须显式指定lob段的存储子句,否则lob段不会随表移动。

lob字段move基础语法

-- 错误示范:这样只搬了表段,lob 段完全不动
alter table your_table move;

-- 正确做法:必须带 lob 子句
alter table your_table move
  lob (lob_column) store as (tablespace target_tablespace);

-- 同时搬迁表段和 lob 段到不同表空间
alter table your_table move tablespace new_table_ts
  lob (lob_column) store as securefile (tablespace new_lob_ts);

-- 多个 lob 列的情况
alter table your_table move
  lob (clob_col1) store as (tablespace lob_ts)
  lob (blob_col2) store as (tablespace lob_ts);

-- 转换为 securefile 并开启压缩和去重(推荐)
alter table your_table move
  lob (lob_column) store as securefile (
    tablespace lob_ts
    compress high
    deduplicate
  );

lob字段move基础案例

-- 创建包含多种lob类型的测试表
create table doc_system (
    doc_id number primary key,
    doc_name varchar2(200),
    doc_content clob,
    doc_image blob
) tablespace users
  lob (doc_content) store as (tablespace users)
  lob (doc_image) store as (tablespace users);

-- 查看当前lob段位置
select segment_name, tablespace_name, segment_type
from user_segments
where segment_name in (
    select segment_name from user_lobs where table_name = 'doc_system'
);

-- 完整move操作(移动数据和所有lob段)
alter table doc_system move
    tablespace data_tbs
    lob (doc_content) store as (tablespace lob_tbs1)
    lob (doc_image) store as (tablespace lob_tbs2)
    parallel 4
    nologging;

-- 关闭并行
alter table doc_system logging noparallel;

-- 重建所有索引
select 'alter index ' || index_name || ' rebuild nologging parallel 4;' 
from user_indexes 
where table_name = 'doc_system' and index_type <>'lob';

-- 执行生成的sql后,关闭并行
select 'alter index ' || index_name || ' logging noparallel;' 
from user_indexes 
where table_name = 'doc_system' and index_type <>'lob';

5、分区表的move策略

对于分区表,可以逐个分区move,减少对整体业务的影响。

-- 查看分区信息
select partition_name, tablespace_name, num_rows
from user_tab_partitions
where table_name = 'big_part_table';

-- 逐个move分区
alter table big_part_table move partition p2024_q1
    tablespace data_tbs
    lob (xml_data) store as (tablespace lob_tbs)
    parallel 2;

-- 重建该分区的本地索引
alter index idx_part_local rebuild partition p2024_q1 parallel 2;

四、案例

环境准备: 创建测试表并模拟碎片

-- 创建测试表
create table test_move as select * from dba_objects;
-- 插入更多数据
insert into test_move select * from test_move;
commit;
insert into test_move select * from test_move;
commit;

-- 创建索引
create index idx_test_move on test_move(object_id);

-- 查看初始状态
select segment_name, blocks, bytes/1024/1024 as size_mb
from user_segments where segment_name = 'test_move';

-- 模拟大量删除(产生碎片)
delete from test_move where rownum <= 100000;
commit;

-- 收集统计信息
exec dbms_stats.gather_table_stats(user, 'test_move');

-- 查看碎片情况
select table_name, num_rows, blocks,
       round(blocks * 8 / 1024, 2) as "hwm_mb",
       round(num_rows * avg_row_len / 1024 / 1024, 2) as "real_data_mb"
from user_tables where table_name = 'test_move';


table_name                       num_rows     blocks     hwm_mb real_data_mb
------------------------------ ---------- ---------- ---------- ------------
test_move                          295036       5060      39.53        22.9

执行 move:

-- 第一步:执行 move(此操作会锁表,全程排他锁)
alter table test_move move;

-- 第二步:检查索引状态(必然失效)
select index_name, status from user_indexes where table_name = 'test_move';
index_name                     status
------------------------------ --------
idx_test_move                  unusable
-- 结果:idx_test_move  →  unusable

-- 第三步:重建索引
alter index idx_test_move rebuild nologging parallel 4;
alter index idx_test_move noparallel logging;  -- 恢复并行度

-- 第四步:恢复表的 logging 和并行度
alter table test_move logging noparallel;

-- 第五步:重新收集统计信息
exec dbms_stats.gather_table_stats(user, 'test_move');

-- 第六步:验证结果
select table_name, num_rows, blocks,
       round(blocks * 8 / 1024, 2) as "hwm_mb",
       round(num_rows * avg_row_len / 1024 / 1024, 2) as "real_data_mb"
from user_tables where table_name = 'test_move';

table_name                       num_rows     blocks     hwm_mb real_data_mb
------------------------------ ---------- ---------- ---------- ------------
test_move                          245036       3577      27.95         22.9

五、lob 字段的碎片处理补充

5.1 lob 存储的特殊性

lob(blob/clob)字段在 oracle 中有独立的存储结构,与普通表数据分开存放:

  • 表段(heap segment):存储普通列数据
  • lob 段(lob segment):存储 lob 数据本身,段名格式为 sys_lobxxxxx$$
  • lob 索引段(lob index):用于定位 lob 数据块,段名格式为 sys_ilxxxxx$$

关键认知:对表执行 alter table ... move 时,默认只处理表段本身,lob 段和 lob 索引段不会被自动处理。这是最常见的"踩坑点"——很多人做完 move 后发现 lob 表空间的空间完全没有释放。

5.2 判断 lob 类型:basicfile 还是 securefile

oracle 有两种 lob 存储类型,处理方式完全不同:

-- 查询 lob 列的存储类型
set linesize 200 pagesize 999
col table_name format a20
col column_name format a12
col segment_name format a25
col tablespace_name format a15
select table_name, column_name, segment_name, securefile, tablespace_name
from dba_lobs
where owner = 'your_schema'
  and table_name = 'your_table';
  • securefile = yes:securefile lob(oracle 11g 默认)
  • securefile = no:basicfile lob(旧式存储)

5.3 查询 lob 段占用空间:

select l.owner, l.table_name, l.column_name, l.segment_name,
       l.tablespace_name, l.securefile,
       round(s.bytes / 1024 / 1024, 2) as "lob_size_mb"
from dba_lobs l
join dba_segments s on l.owner = s.owner and l.segment_name = s.segment_name
where l.owner = 'your_schema'
order by s.bytes desc;

5.4 basicfile lob 的碎片处理

关键语法:必须显式指定 lob (column_name) store as 子句,否则 lob 段不会被处理!

-- 错误示范:这样只搬了表段,lob 段完全不动
alter table your_table move;

-- 正确做法:必须带 lob 子句
alter table your_table move
  lob (lob_column) store as (tablespace target_tablespace);

-- 同时搬迁表段和 lob 段到不同表空间
alter table your_table move tablespace new_table_ts
  lob (lob_column) store as securefile (tablespace new_lob_ts);

-- 多个 lob 列的情况
alter table your_table move
  lob (clob_col1) store as (tablespace lob_ts)
  lob (blob_col2) store as (tablespace lob_ts);

-- 转换为 securefile 并开启压缩和去重(推荐)
alter table your_table move
  lob (lob_column) store as securefile (
    tablespace lob_ts
    compress high
    deduplicate
  );

以上就是oracle表碎片整理方法(alter table move)操作总结的详细内容,更多关于oracle表碎片整理方法的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com