Oracle SYSTEM表空间里有业务对象的处理全流程

 更新时间:2026年10月29日 08:44:53   作者:Lucifer三思而后行  
本文针对Oracle19c RAC多租户环境中业务对象误存于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 的类型分别迁移。

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

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

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

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

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

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

以上就是Oracle SYSTEM表空间里有业务对象的处理全流程的详细内容,更多关于Oracle SYSTEM表空间有业务对象处理的资料请关注脚本之家其它相关文章!

相关文章

  • Oracle中字符串连接的实现方法

    Oracle中字符串连接的实现方法

    Oracle数据库中,使用“||”进行字符串连接,下面就让我们一起了解一下Oracle数据库中字符串连接的方法,希望对您能有所帮助
    2013-05-05
  • Oracle创建自增表分区之按月、按天自增表分区步骤详解

    Oracle创建自增表分区之按月、按天自增表分区步骤详解

    这篇文章主要给大家介绍了关于Oracle创建自增表分区之按月、按天自增表分区的相关资料,Oracle表对数据进行月度分区是一种将数据分组的有效方法,此方法是将数据根据它们所属时间段将其存储为不同分区,需要的朋友可以参考下
    2023-11-11
  • oracle基础语法详解

    oracle基础语法详解

    这篇文章主要介绍了oracle基础语法详解,非常适合Oracle数据库的初步学习,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-05-05
  • Oracle 12c安装方法及一些使用问题

    Oracle 12c安装方法及一些使用问题

    这篇文章主要介绍了Oracle 12c安装方法及一些使用问题,需要的朋友可以参考下
    2016-09-09
  • ORACLE中常用的几种正则表达式小结

    ORACLE中常用的几种正则表达式小结

    相信大家对正则表达式都不陌生,从linux下的命令到Java等编程语言,正则表达式无处不在,下面这篇文章主要给大家介绍了关于ORACLE中常用的几种正则表达式的相关资料,需要的朋友可以参考下
    2022-09-09
  • group by用法详解

    group by用法详解

    本文详细讲解了group by的用法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-12-12
  • Oracle存储过程里操作BLOB的字节数据的办法

    Oracle存储过程里操作BLOB的字节数据的办法

    该篇文章介绍了如何在Oracle存储过程中操作BLOB的字节数据,作者研究了如何获取BLOB的字节长度、如何使用DBMS_LOB包进行BLOB操作、如何进行字节级操作以及如何使用UTL_RAW包,感兴趣的朋友一起看看吧
    2025-03-03
  • 简析Oracle数据库常见问题及解决方案

    简析Oracle数据库常见问题及解决方案

    这篇文章主要介绍了Oracle数据库常见问题及解决方案,总结了一些在Oracle数据库使用过程中的常见问题,并对其进行了分析,给出了解决方案,需要的朋友可以参考下
    2015-08-08
  • Oracle中实现删除重复数据只保留一条

    Oracle中实现删除重复数据只保留一条

    这篇文章主要介绍了Oracle中实现删除重复数据只保留一条,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • Oracle基础教程之分组查询

    Oracle基础教程之分组查询

    这篇文章主要给大家介绍了关于Oracle基础教程之分组查询的相关资料,Oracle分组查询指的是将一张表按照某个字段或几个字段进行分组,并在每个组内进行聚合运算,得到结果集,需要的朋友可以参考下
    2024-02-02

最新评论