在Oracle数据库管理中,表空间使用情况监控是DBA日常工作中不可或缺的一环。特别是当数据库运行时间较长,业务数据不断积累后,用户表的大小增长往往成为影响系统性能的关键因素。我最近接手的一个金融系统就遇到了典型场景:某核心业务表在3个月内从50GB暴涨到300GB,直接导致凌晨批处理作业超时失败。
传统的检查方法是通过手动执行SELECT segment_name, bytes/1024/1024 MB FROM dba_segments WHERE owner='USER1' ORDER BY bytes DESC这类SQL查询,但存在三个明显痛点:
这就是为什么我们需要一个自动化脚本——它不仅能一次性获取所有用户的表空间占用情况,还能生成包含排序、占比分析等维度的标准报告,让DBA快速定位"空间大户"。
脚本的核心数据来自Oracle数据字典视图:
DBA_SEGMENTS:获取段级别存储信息DBA_USERS:关联用户名信息DBA_TABLES:补充表属性信息关键字段包括:
owner:对象所属用户segment_name:段名称(通常是表名)bytes:占用字节数tablespace_name:所属表空间注意:执行脚本的用户需要具有SELECT_CATALOG_ROLE角色或直接授予这些视图的查询权限
sql复制SET SERVEROUTPUT ON SIZE 1000000
SET LINESIZE 200
SET PAGESIZE 0
SET FEEDBACK OFF
SET VERIFY OFF
SPOOL /tmp/table_size_report_&&date..html
PROMPT <html>
PROMPT <head><title>Oracle表空间使用报告</title></head>
PROMPT <style>
PROMPT body { font-family: Arial; margin: 20px; }
PROMPT table { border-collapse: collapse; width: 100%; }
PROMPT th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
PROMPT tr:nth-child(even) { background-color: #f2f2f2; }
PROMPT .warning { background-color: #FFF484 !important; }
PROMPT .critical { background-color: #FFB2B2 !important; }
PROMPT </style>
PROMPT <body>
PROMPT <h2>Oracle用户表空间统计报告</h2>
PROMPT <h3>生成时间: &&date</h3>
BEGIN
DBMS_OUTPUT.PUT_LINE('<table>');
DBMS_OUTPUT.PUT_LINE('<tr><th>排名</th><th>用户名</th><th>表名</th><th>大小(MB)</th><th>占比(%)</th><th>表空间</th><th>最后DDL时间</th></tr>');
FOR rec IN (
WITH user_total AS (
SELECT
owner,
SUM(bytes) AS total_bytes
FROM
dba_segments
WHERE
segment_type IN ('TABLE','TABLE PARTITION','TABLE SUBPARTITION')
GROUP BY
owner
)
SELECT
ROWNUM AS rank,
s.owner,
s.segment_name AS table_name,
ROUND(s.bytes/1024/1024,2) AS size_mb,
ROUND(s.bytes/t.total_bytes*100,2) AS pct,
s.tablespace_name,
t.last_ddl_time
FROM
dba_segments s
JOIN
user_total t ON s.owner = t.owner
JOIN
dba_tables tab ON s.owner = tab.owner AND s.segment_name = tab.table_name
WHERE
s.segment_type IN ('TABLE','TABLE PARTITION','TABLE SUBPARTITION')
ORDER BY
s.bytes DESC
) LOOP
-- 根据大小设置行样式
IF rec.size_mb > 1024 THEN
DBMS_OUTPUT.PUT_LINE('<tr class="critical">');
ELSIF rec.size_mb > 512 THEN
DBMS_OUTPUT.PUT_LINE('<tr class="warning">');
ELSE
DBMS_OUTPUT.PUT_LINE('<tr>');
END IF;
DBMS_OUTPUT.PUT_LINE('<td>'||rec.rank||'</td>');
DBMS_OUTPUT.PUT_LINE('<td>'||rec.owner||'</td>');
DBMS_OUTPUT.PUT_LINE('<td>'||rec.table_name||'</td>');
DBMS_OUTPUT.PUT_LINE('<td>'||rec.size_mb||'</td>');
DBMS_OUTPUT.PUT_LINE('<td>'||rec.pct||'</td>');
DBMS_OUTPUT.PUT_LINE('<td>'||rec.tablespace_name||'</td>');
DBMS_OUTPUT.PUT_LINE('<td>'||TO_CHAR(rec.last_ddl_time,'YYYY-MM-DD HH24:MI')||'</td>');
DBMS_OUTPUT.PUT_LINE('</tr>');
END LOOP;
DBMS_OUTPUT.PUT_LINE('</table>');
END;
/
PROMPT </body>
PROMPT </html>
SPOOL OFF
分层统计设计:
动态样式标记:
HTML报告生成:
sql复制SELECT * FROM session_privs WHERE privilege LIKE '%SELECT%CATALOG%';
若无返回结果,需要DBA授权:
sql复制GRANT SELECT_CATALOG_ROLE TO your_user;
$ORACLE_HOME/bin在PATH中/tmp目录写权限(或修改SPOOL路径)bash复制sqlplus / as sysdba @table_size_report.sql
或指定普通用户:
bash复制sqlplus user/pass@db @table_size_report.sql
生成的HTML报告包含以下关键列:
典型问题识别模式:
当数据库包含超过10万张表时,建议:
sql复制WHERE s.bytes > 10*1024*1024 -- 只统计大于10MB的表
sql复制/*+ PARALLEL(s 4) */
sql复制WHERE s.owner IN ('USER1','USER2')
sql复制UNION ALL
SELECT
ROWNUM + 100000 AS rank,
s.owner,
s.segment_name AS index_name,
ROUND(s.bytes/1024/1024,2) AS size_mb,
NULL AS pct,
s.tablespace_name,
i.last_ddl_time
FROM
dba_segments s
JOIN
dba_indexes i ON s.owner = i.owner AND s.segment_name = i.index_name
sql复制SPOOL /tmp/table_size_report.csv
PROMPT "排名","用户名","表名","大小(MB)","占比(%)","表空间","最后DDL时间"
...
DBMS_OUTPUT.PUT_LINE('"'||rec.rank||'","'||rec.owner||'","'||rec.table_name||'","'||rec.size_mb||'","'||rec.pct||'","'||rec.tablespace_name||'","'||TO_CHAR(rec.last_ddl_time,'YYYY-MM-DD HH24:MI')||'"');
...
bash复制mutt -s "Oracle表空间报告" dba@company.com -a /tmp/table_size_report.html < /dev/null
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| ORA-00942 | 缺少视图权限 | 授予SELECT_CATALOG_ROLE |
| SPOOL写入失败 | 目录权限不足 | 修改为可写目录或使用/tmp |
| 报告乱码 | 字符集不匹配 | 执行SET NLS_LANG=AMERICAN_AMERICA.AL32UTF8 |
| 执行超时 | 表数量过多 | 添加WHERE条件限制范围 |
sql复制EXPLAIN PLAN FOR
WITH user_total AS (...)
SELECT ...;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
sql复制SELECT sid, serial#, sql_id, elapsed_time/1000000 sec
FROM v$session_longops
WHERE time_remaining > 0;
sql复制SELECT tablespace_name, used_space, tablespace_size
FROM dba_temp_space_usage;
修改脚本记录历史数据:
sql复制CREATE TABLE table_size_history AS
SELECT owner, segment_name, bytes, SYSDATE AS check_date
FROM dba_segments WHERE 1=0;
-- 每次执行后插入数据
INSERT INTO table_size_history
SELECT owner, segment_name, bytes, SYSDATE
FROM dba_segments
WHERE segment_type IN ('TABLE','TABLE PARTITION','TABLE SUBPARTITION');
基于规则生成清理脚本:
sql复制SELECT
'ALTER TABLE '||owner||'.'||segment_name||' MOVE TABLESPACE users;' AS move_stmt,
'ALTER TABLE '||owner||'.'||segment_name||' SHRINK SPACE;' AS shrink_stmt
FROM
dba_segments
WHERE
tablespace_name = 'SYSTEM'
AND segment_type IN ('TABLE','TABLE PARTITION','TABLE SUBPARTITION');
将脚本输出导入OEM:
我在实际使用中发现,将此类脚本与日常监控系统结合,可以提前2-3周发现潜在的空间风险。特别是在季度末业务高峰前运行检查,能有效避免因空间不足导致的业务中断。