expdp导出前须停写并核查表空间真实使用率,避免碎片空间被固化;导入时需显式指定REMAP_TABLESPACE、禁用索引重建并单独处理,迁移后验证段是否均匀分布于新表空间。
expdp 导出前必须停写并检查表空间使用率
碎片多的表空间往往伴随高水位和大量空闲但不可用的块,直接导出会把无效空间也“固化”进 dump 文件。导出前务必确认业务已停写,否则 expdp 可能读到不一致数据;同时用 dba_segments 和 dba_free_space 核对实际已用/空闲比例,避免误判——比如某表空间显示 70% 空闲,但全是小于 8k 的碎块,extents 平均大小只有 16k,这种空闲无法被新对象利用。
实操建议:
- 执行
SELECT tablespace_name, SUM(bytes)/1024/1024 AS mb_used FROM dba_segments WHERE tablespace_name = 'YOUR_TS' GROUP BY tablespace_name获取真实已用空间 - 对比
SELECT SUM(bytes)/1024/1024 FROM dba_free_space WHERE tablespace_name = 'YOUR_TS',若差值远小于预期,说明存在严重碎片 - 导出命令中必须加
CONTENT=DATA_ONLY或明确指定INCLUDE=TABLE:"IN (SELECT ...)",避免意外导出统计信息、物化视图日志等冗余对象
重建表空间时别直接 DROP TABLESPACE INCLUDING CONTENTS
直接删表空间看似彻底,但会连带删除数据文件、控制文件记录、甚至影响 RMAN 备份链(如果备份里还引用着该表空间的 SCN)。更危险的是,若表空间里有未显式指定 SEGMENT CREATION DEFERRED 的空表,DROP 后这些表元数据可能残留,后续 impdp 重建时报 ORA-39151: Table exists and cannot be skipped。
实操建议:
- 新建表空间用
CREATE TABLESPACE new_ts DATAFILE '/path/to/new.dbf' SIZE 1G AUTOEXTEND ON NEXT 100M MAXSIZE UNLIMITED EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1M,统一UNIFORM SIZE能从源头减少碎片 - 迁移前先
ALTER USER xxx DEFAULT TABLESPACE new_ts,再逐个移动表:ALTER TABLE owner.tab MOVE TABLESPACE new_ts - 确认所有段迁移完成、旧表空间只剩系统段(如
SYSAUX中的审计表)后,再用DROP TABLESPACE old_ts INCLUDING CONTENTS AND DATAFILES
impdp 导入时启用 REMAP_TABLESPACE 且禁用 INDEXES 参数
即使导出时用了 REMAP_TABLESPACE,导入时仍需显式指定,否则 impdp 会尝试往原表空间写,而它可能已被删或只读。另外,默认导入会重建索引,但索引段在碎片严重的旧空间里建出来仍是碎的;迁移到新表空间后,应让索引也落在新空间,并且用 NOLOGGING + PARALLEL 加速,避免二次碎片。
实操建议:
- 导入命令必须包含
REMAP_TABLESPACE=old_ts:new_ts,多个则用逗号分隔 - 加
TRANSFORM=DISABLE_ARCHIVE_LOGGING:Y和TRANSFORM=SEGMENT_ATTRIBUTES:N,跳过归档日志和段属性继承,由新表空间策略接管 - 索引单独处理:先
impdp ... EXCLUDE=INDEX导入表数据,再用SELECT 'CREATE INDEX ' || index_name || ' ON ' || table_owner || '.' || table_name || ' ... TABLESPACE new_ts NOLOGGING PARALLEL 4;' FROM dba_indexes WHERE tablespace_name = 'old_ts'生成建索引语句
重建后验证索引段是否真正落在新表空间
经常有人以为 REMAP_TABLESPACE 万无一失,结果查 dba_indexes 发现 tablespace_name 是对的,但 dba_segments 里对应索引段的 tablespace_name 还是旧的——这是因为索引重建没触发,或者 impdp 时用了 SKIP_UNUSABLE_INDEXES 却没留意日志里的警告。碎片清理效果全看段是否真在新空间里均匀分布。
实操建议:
- 导入完成后立刻运行
SELECT segment_name, segment_type, tablespace_name, bytes/1024/1024 AS mb FROM dba_segments WHERE tablespace_name = 'new_ts' AND segment_type IN ('TABLE','INDEX') ORDER BY bytes DESC - 重点看最大几个索引段的
bytes是否接近UNIFORM SIZE的整数倍(比如设了 1M uniform,那索引段大小应是 1M、2M、4M…),若出现 1.23M、3.87M 这类非整数倍,说明仍有碎片,得重新ALTER INDEX ... REBUILD TABLESPACE new_ts NOLOGGING - 别信
dba_extents的 count 总数,要看max(extent_id) - min(extent_id)是否接近count(*),差太多就代表 extent 不连续
最麻烦的不是操作步骤,而是中间任意一环漏掉校验——比如导出后没清空回收站、重建时忘了改用户默认表空间、导入时参数拼错一个字母。每个 expdp/impdp 命令跑完,都得翻日志末尾三行,确认 “successfully completed” 前没有 “failed” 或 “skipped”。










