DBA_TABLESPACES和DBA_DATA_FILES无法准确计算使用率,因前者仅存元信息、后者未涵盖空文件、临时表空间及自动扩展状态;必须联合DBA_FREE_SPACE(NVL处理空文件)、V$TEMP_SPACE_HEADER(临时表空间)并区分CONTENTS类型,再通过存储过程封装双判告警逻辑。
dba_tablespaces 和 dba_data_files 查出来的使用率不准,直接算容易漏掉空文件、临时表空间、自动扩展禁用等关键场景。真要落地告警,必须用存储过程封装逻辑,再由外部调度触发。
为什么不能只查 dba_free_space 算使用率?
因为 dba_free_space 对完全没分配过区(extents)的空数据文件不返回任何记录,导致 free_bytes = 0,使用率被误算为 100%;而实际该文件还有全部空间可用。
临时表空间压根不走 dba_free_space,得查 v$temp_space_header;UNDO 和 TEMP 表空间在 EM 里根本不能设阈值,必须单独处理。
更麻烦的是:maxbytes 字段在 autoextensible = 'NO' 时是假上限——真实可用空间就是 bytes,不是 maxbytes。
存储过程里怎么算准已用空间?
核心是三张视图联动:dba_tablespaces + dba_data_files(按表空间聚合 bytes / maxbytes / autoextensible)+ dba_free_space(sum free_bytes),再补上临时表空间专用逻辑:
- 对永久表空间:已用 =
SUM(d.bytes) - NVL(SUM(f.bytes), 0),其中f.bytes来自dba_free_space - 对空文件:
dba_free_space没记录 →NVL(SUM(f.bytes), 0)返回 0 → 已用 =SUM(d.bytes),正确 - 对临时表空间:改查
v$temp_space_header,用used_blocks * block_size算已用,别碰dba_free_space - 自动扩展状态必须显式输出,因为
autoextensible = 'NO'时,哪怕使用率才 70%,也可能马上撑爆
示例片段(简化版):
SELECT
t.tablespace_name,
t.contents,
ROUND((d.total_bytes - NVL(f.free_bytes, 0)) / d.total_bytes * 100, 2) AS used_pct,
d.autoextensible,
d.max_bytes / 1024 / 1024 AS max_mb,
(d.total_bytes - NVL(f.free_bytes, 0)) / 1024 / 1024 AS used_mb
FROM dba_tablespaces t
JOIN (
SELECT tablespace_name,
SUM(bytes) total_bytes,
SUM(decode(autoextensible, 'YES', maxbytes, bytes)) max_bytes,
MAX(autoextensible) autoextensible
FROM dba_data_files
GROUP BY tablespace_name
) d ON t.tablespace_name = d.tablespace_name
LEFT JOIN (
SELECT tablespace_name, SUM(bytes) free_bytes
FROM dba_free_space
GROUP BY tablespace_name
) f ON t.tablespace_name = f.tablespace_name
WHERE t.contents != 'TEMPORARY';告警逻辑怎么嵌进存储过程里?
不能只返回结果集,得做判断、写日志、发通知。关键点:
- 告警阈值必须可配——建议建配置表
ALERT_CFG_DB_TABLESPACE,字段含TS_NAME、ALERT_RATIO(如 85)、ALERT_FREE_GB(如 5) - 每轮检查都 INSERT 到日志表
ALERT_LOG_DB_TABLESPACE,带INFO_TIME和TS_RATIO,方便回溯趋势 - 触发条件要“双判”:已用率超阈值 且 剩余空间低于绝对值(比如剩 2GB 但用了 92%,比剩 20GB 但用了 96% 更紧急)
- 邮件发送用
UTL_MAIL.SEND,但必须提前执行ALTER SYSTEM SET smtp_out_server='xxx' SCOPE=BOTH,否则静默失败 - 临时表空间单独分支处理,阈值建议设更低(如 75%),因为排序/哈希操作突发增长快,来不及扩容
调用和调度要注意什么?
Python 或 shell 脚本调用时,别用 sqlplus -S 直接跑 SELECT —— 那只是查,不是告警。必须 EXEC 存储过程:
sqlplus -S / as sysdba <<EOF EXEC MONITOR_TABLESPACE_ALERT; EXIT; EOF
调度频率取决于业务压力:OLTP 系统建议 10 分钟一次;报表类可放宽到 30 分钟。
最容易被忽略的是权限:存储过程里查 dba_* 视图,执行用户必须有 SELECT ANY DICTIONARY,且 UTL_MAIL 需显式授权:GRANT EXECUTE ON UTL_MAIL TO your_user;
另外,如果数据库启用了 Oracle Vault 或细粒度审计,dba_free_space 可能被限制访问,得确认策略是否放行。


















