现象:数据库表空间的使用率看起来并不高,但其对应的数据文件却非常大,导致磁盘空间告急。常规的ALTER TABLESPACE ... RESIZE DATAFILE ... TO ...命令在尝试收缩数据文件时报错,无法完成。
核心原因:表空间的高水位线(High Water Mark, HWM)之上存在大量已释放但未回收的空间。直接收缩文件会触及其下的数据簇(Extent),因此被达梦机制阻止。
报错1:表空间上有事务未提交。
处理方式:查询并终止阻塞会话。通过关联V$SESSIONS和V$LOCK,找到持有事务的会话ID,并使用SP_CLOSE_SESSION系统过程强制关闭。
sql
-- 生成关闭会话的语句
SELECT 'SP_CLOSE_SESSION(''' || sess_id || ''');'
FROM V$SESSIONS
WHERE sess_id != SESSID()
AND trx_id IN (SELECT trx_id FROM V$LOCK WHERE table_id = 0 AND trx_id <> 0);
报错2:无法回收簇。
这是核心难点,即RESIZE操作无法越过文件尾部的已占用簇。需要通过一系列系统视图找到“占位”对象。
定位步骤:
V$DATAFILE中找到目标数据文件的GROUP_ID(即表空间ID)。V$EXTENTS,按EXTENT_ID降序排列,找出文件尾部(例如EXTENT_ID > 300000)且状态不为FREE的簇记录,记录下这些簇的SEG_ID。V$SEGMENT_INFOS将SEG_ID映射为OBJ_ID,最后在DBA_OBJECTS中确认对象的名称、类型和所属模式。select *from SYS.V$DATAFILE;--group_id=17
select *from V$EXTENTS where ts_id=17 order by extent_id desc --使用数据文件的ts_id查SEG_ID
select *from v$SEGMENT_INFOS where SEG_ID='111706' --查obj_id33591173
select *from dba_objects where object_id='33627934';
---批量查找
select * from dba_objects where object_id in (
select distinct obj_id from v$segment_infos where seg_id in(
select seg_id from v$extents where ts_id=17 and state<>'FREE' and seg_id>0 and extent_id >300000
order by extent_id desc
));
---处理
--重构聚集索引。
SP_REORGANIZE_INDEX('CCRM','INDEX33573599');
--重构非聚集索引
ALTER INDEX CCRM.ACRM_F_CI_CST_ADDR_INFO_PK REBUILD ONLINE;
SP_REBUILD_INDEX(SCHEMA_NAME varchar(256), INDEX_ID int);
---移动表空间
ALTER TABLE 模式名.表名 MOVE TABLESPACE 目标表空间名;
--批量
SELECT
'ALTER TABLE ' || OWNER || '.' || TABLE_NAME || ' MOVE TABLESPACE 目标表空间名;' AS MOVE_SQL
FROM
DBA_TABLES
WHERE
OWNER = '你的模式名' -- 例如 'SYSDBA'
AND TABLESPACE_NAME = '当前表空间名'; -- 可选,指定从哪个表空间迁出
1. 普通索引会失效,需要重建,需要重新收集统计信息
SELECT OWNER, INDEX_NAME FROM ALL_INDEXES WHERE STATUS = 'UNUSABLE';
处理策略:本次案例中发现有31个对象(表、物化视图等)位于尾部区域。
MOVE到其他表空间,将临时性的物化视图删除。CALL SP_RECLAIM_TS_FREE_EXTENTS('表空间名');重组空闲簇。若仍无法回收,可在业务低峰期尝试重启数据库。清理完尾部对象后,需要计算能将文件收缩到的最小安全值(即高水位线HWM处的大小)
SELECT
c.name AS tablespace_name,
a.path AS file_name,
CEIL( NVL(b.hwm, 1) * (page/1024.0/1024.0) ) AS "smallest(Mb) - HWM", -- 可以resize到的最小值(MB)
a.TOTAL_SIZE * (page/1024.0/1024.0) AS "currsize(Mb)",
a.TOTAL_SIZE * (page/1024.0/1024.0) - NVL(b.hwm, 1) * (page/1024.0/1024.0) AS "savings(Mb)"
FROM V$datafile a
LEFT JOIN (
SELECT ts_id, file_id,
MAX(1.0 * EXTENT_ID * SF_GET_EXTENT_SIZE() + USED) AS hwm
FROM V$EXTENTS
WHERE state NOT IN ('FREE') AND seg_id > 0
GROUP BY ts_id, file_id
) b ON (a.id = b.file_id AND a.group_id = b.ts_id)
JOIN V$tablespace c ON a.GROUP_ID = c.ID
WHERE a.status$ != 0
AND a.path LIKE '%你的数据文件名%' -- 替换为实际文件
ORDER BY "smallest(Mb) - HWM" DESC;
查询结果中的smallest(Mb) - HWM就是理论上可以RESIZE到的目标值。例如,计算得出目标值为279397MB,则可执行:
ALTER TABLESPACE "表空间名" RESIZE DATAFILE '数据文件名' TO 279397;
文章
阅读量
获赞
