查表空间使用率不能只看DBA_TABLESPACES,因其仅存状态和类型,需关联DBA_DATA_FILES与DBA_SEGMENTS计算;否则报表全为0%或报ORA-01476错误。
查表空间使用率为什么不能只看 DBA_TABLESPACES
因为 dba_tablespaces 只存表空间状态和类型,不存实际占用数据。真实使用率必须关联 dba_data_files(物理文件大小)和 dba_segments(已分配段空间),否则导出的报表全是 0% 或报错。
常见错误现象:脚本跑出来所有表空间使用率都是 0,或者 ORA-01476: divisor is equal to zero —— 这是因为某些表空间没数据文件(比如只含临时文件),或文件 BYTES 为 0。
- 务必用
DBA_DATA_FILES做主表,过滤掉STATUS != 'AVAILABLE'的异常文件 - 对空表空间(无 segment)要补
NVL(SUM(bytes), 0),避免 NULL 导致整行丢失 - 注意单位统一:所有
BYTES都除以 1024/1024 转 MB,再保留一位小数,别混用 GB 和 KB
用一条 SQL 拿出可用率+告警标记的完整报表
核心逻辑是:已用 = 总大小 − 剩余空间,而剩余空间来自 DBA_FREE_SPACE;但该视图在大文件表空间(bigfile)下可能缺失记录,所以更稳的方式是用 DBA_DATA_FILES.BYTES - NVL(<code>DBA_SEGMENTS.SUM(BYTES), 0) 估算已用(近似值,适用于巡检场景)。
以下 SQL 直接输出带告警色标的文本报表(可重定向到 .csv 或直接 spool):
SELECT
t.tablespace_name,
ROUND(d.totalsize / 1024 / 1024, 1) AS "TOTAL_MB",
ROUND(NVL(s.usedsize, 0) / 1024 / 1024, 1) AS "USED_MB",
ROUND((NVL(s.usedsize, 0) / d.totalsize) * 100, 1) AS "PCT_USED",
CASE
WHEN (NVL(s.usedsize, 0) / d.totalsize) >= 0.9 THEN 'CRITICAL'
WHEN (NVL(s.usedsize, 0) / d.totalsize) >= 0.8 THEN 'WARNING'
ELSE 'OK'
END AS "STATUS"
FROM DBA_TABLESPACES t
JOIN (
SELECT tablespace_name, SUM(bytes) AS totalsize
FROM DBA_DATA_FILES
WHERE STATUS = 'AVAILABLE'
GROUP BY tablespace_name
) d ON t.tablespace_name = d.tablespace_name
LEFT JOIN (
SELECT tablespace_name, SUM(bytes) AS usedsize
FROM DBA_SEGMENTS
GROUP BY tablespace_name
) s ON t.tablespace_name = s.tablespace_name
WHERE t.contents = 'PERMANENT'
ORDER BY "PCT_USED" DESC;注意:DBA_SEGMENTS 不包含临时段、回滚段(UNDO)和 Oracle 内部对象(如 SYS.AUD$ 分区),所以这个“已用”是业务数据 + 索引等常规段的占用,对巡检足够;若需含 UNDO,得额外联 DBA_UNDO_EXTENTS。
spool 导出时为什么中文乱码或字段截断
Oracle SQL*Plus 默认字符集依赖客户端 NLS_LANG,导出 CSV 时还容易因字段含换行或逗号崩格式。最简解法是关格式化、强制列宽、用制表符分隔:
- 执行前加:
SET LINESIZE 300 PAGESIZE 0 FEEDBACK OFF VERIFY OFF TRIMSPOOL ON - 用
COL tablespace_name FORMAT A20显式设列宽,防截断 - 导出命令用
SPOOL /tmp/tbs_usage.csv,别用 Windows 路径(反斜杠会触发转义) - 关键一步:在 SELECT 前加
SELECT '"' || t.tablespace_name || '"', ...手动加双引号包字段,兼容 Excel 解析
如果目标是自动化定时任务,建议用 sqlplus -S / as sysdba @tbs_check.sql 调用,且脚本末尾写 EXIT,避免卡住。
自动巡检脚本绕不开的三个权限和两个坑
没有 SELECT_CATALOG_ROLE 或直接授予 SELECT on DBA_DATA_FILES 等视图,脚本一定报 ORA-00942: table or view does not exist。DBA 角色默认有,但普通监控账号必须显式赋权。
- 坑一:
DBA_FREE_SPACE在启用 ASSM(自动段空间管理)的表空间里,空闲块统计有延迟,刚删大表后立即查可能不准 —— 巡检频率别低于 1 小时 - 坑二:如果数据库启用了加密表空间(ENCRYPTION),
DBA_DATA_FILES.BYTES是明文大小,但实际磁盘占用可能更大(加盐、头信息),报表仅反映逻辑使用率,不反映物理磁盘压力 - 必须检查
V$DATABASE.OPEN_MODE是否为READ WRITE,备库上跑这个脚本会查不到最新 segment 数据
真正难的不是写出这条 SQL,而是让同一份脚本在 11g、12c、19c 上都稳定返回数值——DBA_SEGMENTS 在 12c+ 多租户中默认只查当前 PDB,跨容器得用 CDB_SEGMENTS,这点很容易被忽略。


















