Oracle SYSTEM 表空间里有业务对象,怎么处理?
前言
今天收到一条 Oracle 数据库告警:
APP_PDB(4):ORA-1654: unable to extend index APP.IDX_ORDER_DETAIL
by 8192 in tablespace SYSTEM [APP_PDB]
看到 ORA-01654,第一反应就是表空间空间不足。不过仔细看了一眼,发现报错的不是普通业务表空间,而是 SYSTEM。
这就有点不对劲了。
SYSTEM 主要用于存放 Oracle 数据字典等系统对象,正常情况下,业务表和索引不应该放在这里。
于是我登录数据库检查了一下,发现某业务 PDB 的 SYSTEM 已经超过 200GB,其中一个业务索引就占了 100GB 左右。继续检查其他 PDB,发现不只是一个业务系统,另外几个 PDB 也有业务表、索引和 LOB 存放在 SYSTEM 中。
既然发现了,干脆一起处理掉。
下面记录一下这次 Oracle 19c RAC 环境下的检查和迁移过程。
SYSTEM 里面到底放了多少业务对象?
这次的数据库环境是 Oracle 19c RAC,采用 CDB/PDB 架构。
先进入对应的业务 PDB,检查 SYSTEM 表空间大小:
ALTER SESSION SET CONTAINER=APP_PDB;
SELECT
TABLESPACE_NAME,
ROUND(SUM(BYTES)/POWER(1024,3),2) TOTAL_GB
FROM DBA_DATA_FILES
WHERE TABLESPACE_NAME='SYSTEM'
GROUP BY TABLESPACE_NAME;
检查剩余空间:
SELECT
TABLESPACE_NAME,
ROUND(SUM(BYTES)/POWER(1024,3),2) FREE_GB
FROM DBA_FREE_SPACE
WHERE TABLESPACE_NAME='SYSTEM'
GROUP BY TABLESPACE_NAME;
接着查看 SYSTEM 中有哪些对象:
SELECT
OWNER,
SEGMENT_TYPE,
COUNT(*) SEGMENT_COUNT,
ROUND(SUM(BYTES)/POWER(1024,3),2) SIZE_GB
FROM DBA_SEGMENTS
WHERE TABLESPACE_NAME='SYSTEM'
GROUP BY OWNER,SEGMENT_TYPE
ORDER BY SIZE_GB DESC;
检查发现,几个业务 PDB 都存在业务对象落入 SYSTEM 的情况,主要包括普通表、索引和 LOB。
其中一个业务索引超过 100GB,对应业务表的数据量也比较大。
为了避免遗漏其他 PDB,可以直接从 CDB 层面统一检查:
ALTER SESSION SET CONTAINER=CDB$ROOT;
SELECT
C.NAME AS PDB_NAME,
S.OWNER,
S.SEGMENT_TYPE,
COUNT(*) SEGMENT_COUNT,
ROUND(SUM(S.BYTES)/POWER(1024,3),2) SIZE_GB
FROM CDB_SEGMENTS S
JOIN V$CONTAINERS C
ON C.CON_ID=S.CON_ID
JOIN CDB_USERS U
ON U.CON_ID=S.CON_ID
AND U.USERNAME=S.OWNER
WHERE S.TABLESPACE_NAME='SYSTEM'
AND U.ORACLE_MAINTAINED='N'
GROUP BY C.NAME,S.OWNER,S.SEGMENT_TYPE
ORDER BY SIZE_GB DESC;
这里通过 ORACLE_MAINTAINED='N' 排除 Oracle 维护用户,重点检查业务 Schema。
确认对象分布后,再逐个 PDB 处理。
先检查业务用户的默认表空间
除了迁移已有对象,还需要确认业务用户的默认表空间配置:
SELECT
USERNAME,
DEFAULT_TABLESPACE,
TEMPORARY_TABLESPACE
FROM DBA_USERS
WHERE ORACLE_MAINTAINED='N'
ORDER BY USERNAME;
如果业务用户的默认表空间配置不合理,需要调整到对应的业务表空间。
例如:
ALTER USER APP
DEFAULT TABLESPACE APP_DATA_TBS;
ALTER USER APP_REPORT
DEFAULT TABLESPACE APP_REPORT_TBS;
这里要注意,修改默认表空间不会自动迁移已经存在的对象。
另外,即使用户默认表空间正确,如果建表或建索引时显式指定了 SYSTEM,业务对象仍然可能进入 SYSTEM。
所以除了调整用户配置,后续建表、建索引脚本也应该一起检查。
普通表、索引和 LOB 怎么迁移?
对于已经落入 SYSTEM 的业务对象,需要根据对象类型分别处理。
索引迁移
普通 B-tree 索引可以使用 REBUILD ONLINE 迁移到业务表空间:
ALTER INDEX APP.IDX_ORDER_ID
REBUILD ONLINE
TABLESPACE APP_INDEX_TBS;
ONLINE 可以尽量减少重建期间对业务 DML 的影响,不过不代表完全没有锁等待,生产环境还是建议选择业务低峰期执行。
迁移完成后检查:
SELECT
OWNER,
INDEX_NAME,
TABLESPACE_NAME,
STATUS
FROM DBA_INDEXES
WHERE OWNER='APP';
确认索引已经进入目标表空间,状态为 VALID。
普通表迁移
Oracle 19c 对符合条件的普通堆表支持在线移动:
ALTER TABLE APP.ORDER_DETAIL
MOVE ONLINE
TABLESPACE APP_DATA_TBS;
迁移完成后检查:
SELECT
OWNER,
TABLE_NAME,
TABLESPACE_NAME
FROM DBA_TABLES
WHERE OWNER='APP'
AND TABLE_NAME='ORDER_DETAIL';
对于分区表或不支持在线移动的对象,需要根据实际情况制定迁移方案。
LOB 迁移
LOB 需要单独检查。
部分表虽然已经迁移到了业务表空间,但对应的 LOB 数据段可能仍然留在原来的表空间。
先查询:
SELECT
OWNER,
TABLE_NAME,
COLUMN_NAME,
SEGMENT_NAME,
TABLESPACE_NAME
FROM DBA_LOBS
WHERE OWNER='APP'
AND TABLESPACE_NAME='SYSTEM';
对于符合条件的 LOB,可以使用:
ALTER TABLE APP.DOC_DETAIL
MOVE LOB (DOC_CONTENT)
STORE AS (
TABLESPACE APP_DATA_TBS
);
具体执行方式需要结合 LOB 类型和存储属性确认。
表、索引、LOB 迁移完成后,再通过 DBA_SEGMENTS 检查 SYSTEM 是否还有业务段残留。
上百 GB 的大索引怎么迁移?
普通对象处理完成后,接下来就是那个超过 100GB 的大索引。
这种体量的索引,我一般不会直接执行 REBUILD,而是先检查目标表空间、ASM 和 TEMP 容量。
准备目标索引表空间
本次单独规划了业务索引表空间,使用 ASM +DATA 磁盘组:
CREATE TABLESPACE APP_INDEX_TBS
DATAFILE '+DATA'
SIZE 1G
AUTOEXTEND ON NEXT 1G MAXSIZE 30G;
根据实际容量需求继续添加数据文件:
ALTER TABLESPACE APP_INDEX_TBS
ADD DATAFILE '+DATA'
SIZE 1G
AUTOEXTEND ON NEXT 1G MAXSIZE 30G;
检查表空间容量:
SELECT
TABLESPACE_NAME,
ROUND(SUM(BYTES)/POWER(1024,3),2) CURRENT_GB,
ROUND(SUM(
CASE
WHEN AUTOEXTENSIBLE='YES'
THEN MAXBYTES
ELSE BYTES
END
)/POWER(1024,3),2) MAX_GB
FROM DBA_DATA_FILES
WHERE TABLESPACE_NAME='APP_INDEX_TBS'
GROUP BY TABLESPACE_NAME;
同时检查 ASM 磁盘组可用空间。
在 ASM 实例执行:
SELECT
NAME,
TOTAL_MB,
FREE_MB,
USABLE_FILE_MB
FROM V$ASM_DISKGROUP_STAT
WHERE NAME='DATA';
目标表空间容量需要结合原索引大小和后续增长情况评估。
检查 TEMP 容量
大索引重建过程中,Oracle 可能需要使用大量 TEMP 空间。
先检查 TEMPFILE:
SELECT
FILE_ID,
FILE_NAME,
ROUND(BYTES/POWER(1024,3),2) SIZE_GB,
AUTOEXTENSIBLE,
ROUND(MAXBYTES/POWER(1024,3),2) MAX_GB
FROM DBA_TEMP_FILES
ORDER BY FILE_ID;
如果 TEMP 容量不足,可以根据评估结果提前增加临时文件:
ALTER TABLESPACE TEMP
ADD TEMPFILE '+DATA'
SIZE 30G
AUTOEXTEND OFF;
这里的 30GB 只是示例,实际大小需要结合索引规模、ASM 可用空间和并发业务负载确定。
对于上百 GB 的索引重建,TEMP 可能消耗几十 GB 甚至更多,不能只关注目标索引表空间。
执行在线重建
准备完成后执行:
ALTER INDEX APP.IDX_BIG_ORDER
REBUILD ONLINE
TABLESPACE APP_INDEX_TBS;
在线重建可以尽可能减少对正常业务的影响。
不过,大索引重建仍然会产生较大的 I/O 和空间消耗,建议安排在业务低峰期,并持续监控。
大索引重建过程中看哪些指标?
我一般重点关注三个方面:执行会话、重建进度和空间使用量。
查看执行会话
SELECT
INST_ID,
SID,
SERIAL#,
STATUS,
SQL_ID,
EVENT,
WAIT_CLASS,
LAST_CALL_ET
FROM GV$SESSION
WHERE SQL_ID='&SQL_ID';
RAC 环境使用 GV$,可以同时查看各实例的会话信息。
查看重建进度
定位到执行会话后,通过 GV$SESSION_LONGOPS 查看长操作。
SELECT
INST_ID,
SID,
SERIAL#,
OPNAME,
ROUND(
SOFAR*100/NULLIF(TOTALWORK,0),2
) PCT,
ELAPSED_SECONDS,
TIME_REMAINING,
MESSAGE
FROM GV$SESSION_LONGOPS
WHERE INST_ID=&INST_ID
AND SID=&SID
AND SERIAL#=&SERIAL
ORDER BY START_TIME DESC;
索引重建期间,可能看到 Table Scan、Sort Output 等阶段。
Table Scan 主要反映底表扫描进度,后续还可能涉及排序输出、索引构建和最终切换。
这里需要注意:
GV$SESSION_LONGOPS中某个阶段达到 100%,并不代表整个索引重建已经完成。
最终仍然需要确认 DDL 执行结果和索引状态。
查看 TEMP 使用情况
SELECT
INST_ID,
SEGTYPE,
ROUND(SUM(BLOCKS)*8192/POWER(1024,3),2) USED_GB
FROM GV$TEMPSEG_USAGE
WHERE CON_ID=TO_NUMBER(
SYS_CONTEXT('USERENV','CON_ID')
)
GROUP BY INST_ID,SEGTYPE
ORDER BY USED_GB DESC;
这里按照 8KB 块大小计算,实际使用时应确认 TEMP 表空间的块大小。
对于 RAC,建议检查所有实例的 TEMP 使用情况。
查看目标表空间剩余容量
SELECT
TABLESPACE_NAME,
ROUND(SUM(BYTES)/POWER(1024,3),2) FREE_GB
FROM DBA_FREE_SPACE
WHERE TABLESPACE_NAME='APP_INDEX_TBS'
GROUP BY TABLESPACE_NAME;
重建过程中,目标表空间的空闲容量可能持续减少。
如果 TEMP 或目标表空间接近容量上限,需要及时评估扩容,避免空间不足导致重建失败。
迁移完成后怎么验证?
对象迁移完成后,除了确认 SQL 执行成功,还需要检查索引状态和 SYSTEM 中的业务对象残留。
先检查索引:
SELECT
OWNER,
INDEX_NAME,
TABLESPACE_NAME,
STATUS
FROM DBA_INDEXES
WHERE OWNER='APP'
AND INDEX_NAME='IDX_BIG_ORDER';
确认索引已经进入目标表空间,状态为 VALID。
再检查 SYSTEM:
SELECT
OWNER,
SEGMENT_NAME,
SEGMENT_TYPE,
ROUND(BYTES/POWER(1024,3),2) SIZE_GB
FROM DBA_SEGMENTS
WHERE TABLESPACE_NAME='SYSTEM'
AND OWNER='APP'
ORDER BY BYTES DESC;
最后回到 CDB ROOT,再执行一次跨容器检查:
ALTER SESSION SET CONTAINER=CDB$ROOT;
SELECT
C.NAME AS PDB_NAME,
S.OWNER,
S.SEGMENT_TYPE,
COUNT(*) SEGMENT_COUNT,
ROUND(SUM(S.BYTES)/POWER(1024,3),2) SIZE_GB
FROM CDB_SEGMENTS S
JOIN V$CONTAINERS C
ON C.CON_ID=S.CON_ID
JOIN CDB_USERS U
ON U.CON_ID=S.CON_ID
AND U.USERNAME=S.OWNER
WHERE S.TABLESPACE_NAME='SYSTEM'
AND U.ORACLE_MAINTAINED='N'
GROUP BY C.NAME,S.OWNER,S.SEGMENT_TYPE
ORDER BY SIZE_GB DESC;
本次最终检查没有发现非 Oracle 维护用户在 SYSTEM 中的残留段。
另外,最大的业务索引在线重建后,段占用空间也有所减少,索引状态保持 VALID。
需要说明的是,索引重建后的空间变化与原索引的存储情况有关,并不是所有索引重建后都会明显缩小。
SYSTEM 空间释放后,还需要做什么?
业务对象迁出 SYSTEM 后,表空间内部的空闲空间已经释放。
但这里需要区分两个概念:
SYSTEM 表空间空闲,不代表 ASM 磁盘空间已经回收。
如果后续需要回收 ASM 容量,还需要检查 SYSTEM 数据文件的高水位,确认安全的收缩范围。
SYSTEM 属于核心系统表空间,不建议仅根据空闲空间大小直接执行 RESIZE。
另外,索引重建期间新增的 TEMPFILE,也需要在确认临时段释放后,结合正常业务峰值决定是否回收。
这些属于后续容量优化,建议单独安排变更窗口处理。
总结
这次从一条 SYSTEM 表空间告警开始,检查发现多个业务 PDB 都存在业务对象存放不合理的情况。
整个处理过程就是先确认对象分布,再根据表、索引和 LOB 的类型分别迁移。
- 对于普通对象,Oracle 19c 提供的在线迁移能力可以减少对业务的影响。
- 对于上百 GB 的大索引,重点还是执行前的空间规划,以及重建期间对 TEMP、ASM 和执行进度的监控。
最终,相关业务对象全部迁出 SYSTEM,索引在线重建完成,SYSTEM 中的业务段也清理干净了。
这次处理下来,我觉得最值得注意的不是某一条迁移命令,而是日常巡检中容易忽略的一项检查。
SYSTEM 表空间使用率正常,不代表里面就没有业务对象。
以后做数据库接管或者日常巡检,我还是建议顺手查一下 SYSTEM 中有没有非 Oracle 维护用户的段。
这种问题早点发现,处理起来会简单很多。
- 点赞
- 收藏
- 关注作者
评论(0)