Oracle SYSTEM 表空间里有业务对象,怎么处理?

举报
Lucifer三思而后行 发表于 2026/10/26 13:41:10 2026/10/26
【摘要】 前言 今天收到一条 Oracle 数据库告警: APPPDB(4):ORA-1654: unable to extend index APP.IDXORDERDETAIL by 8192 in tablespace SYSTEM [APPPDB] 看到 ORA-01654,第一反应就是表空间空间不足

前言

今天收到一条 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 的类型分别迁移。

  1. 对于普通对象,Oracle 19c 提供的在线迁移能力可以减少对业务的影响。
  2. 对于上百 GB 的大索引,重点还是执行前的空间规划,以及重建期间对 TEMP、ASM 和执行进度的监控。

最终,相关业务对象全部迁出 SYSTEM,索引在线重建完成,SYSTEM 中的业务段也清理干净了。

这次处理下来,我觉得最值得注意的不是某一条迁移命令,而是日常巡检中容易忽略的一项检查。

SYSTEM 表空间使用率正常,不代表里面就没有业务对象。

以后做数据库接管或者日常巡检,我还是建议顺手查一下 SYSTEM 中有没有非 Oracle 维护用户的段。

这种问题早点发现,处理起来会简单很多。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。