我在 ORACLE 中通过 DBLINKS 开发了这个 TEMP 表空间监视器,如果任何临时表空间达到 v_used 调用的任何百分比,它会发送电子邮件:
CREATE OR REPLACE PROCEDURE SP_TEMP_MON(
v_used NUMBER
) AS
v_sql VARCHAR2(4000);
v_html CLOB := EMPTY_CLOB();
v_execution_date DATE := SYSDATE;
v_db_link_name VARCHAR2(128);
v_link_open BOOLEAN := FALSE;
v_actions_found BOOLEAN := FALSE;
v_total_mb NUMBER;
v_query_mb_used NUMBER;
v_tablespace_mb_used NUMBER;
v_query_percentage_used NUMBER;
v_tablespace_percentage_used NUMBER;
TYPE temp_details_type IS RECORD (
tablespace VARCHAR2(128),
os_username VARCHAR2(128),
sql_text VARCHAR2(4000),
query_mb_used NUMBER,
tablespace_mb_used NUMBER,
total_mb NUMBER
);
TYPE temp_details_table IS TABLE OF temp_details_type;
temp_details_list temp_details_table := temp_details_table();
CURSOR c_dblinks IS
SELECT NOMBREDBLINK || '.DBLINK.DOMAIN.COM' AS NOMBREDBLINK
FROM BDHIST.CATALOGO_DBLINKS
WHERE CONECTA = 'SI' AND NODO IS NULL;
BEGIN
-- We iterate through all the user DBLINKs defined in the database to audit.
FOR rec_link IN c_dblinks LOOP
v_db_link_name := rec_link.NOMBREDBLINK;
v_link_open := FALSE;
BEGIN
-- Building the dynamic query to obtain tablespace usage information
v_sql := 'WITH sort_usage AS (
SELECT
T.tablespace,
SUM(T.blocks * TBS.block_size) / 1024 / 1024 AS mb_used,
S.osuser,
Q.sql_text
FROM
v$sort_usage@'||v_db_link_name||' T
JOIN
v$session@'||v_db_link_name||' S ON T.session_addr = S.saddr
LEFT JOIN
v$sqlarea@'||v_db_link_name||' Q ON T.sqladdr = Q.address
JOIN
dba_tablespaces@'||v_db_link_name||' TBS ON T.tablespace = TBS.tablespace_name
GROUP BY
T.tablespace, S.osuser, Q.sql_text
),
tablespace_summary AS (
SELECT
A.tablespace_name AS tablespace,
SUM(A.used_blocks * D.block_size) / 1024 / 1024 AS mb_used,
SUM(D.mb_total) AS total_mb
FROM
v$sort_segment@'||v_db_link_name||' A
JOIN
(SELECT
B.name,
C.block_size,
SUM(C.bytes) / 1024 / 1024 AS mb_total
FROM
v$tablespace@'||v_db_link_name||' B
JOIN
v$tempfile@'||v_db_link_name||' C ON B.ts# = C.ts#
GROUP BY
B.name,
C.block_size) D ON A.tablespace_name = D.name
GROUP BY
A.tablespace_name
)
SELECT
ts.tablespace,
su.osuser,
su.sql_text,
su.mb_used AS query_mb_used,
ts.mb_used AS tablespace_mb_used,
ts.total_mb
FROM
tablespace_summary ts
JOIN
sort_usage su ON ts.tablespace = su.tablespace
ORDER BY
query_mb_used DESC';
EXECUTE IMMEDIATE v_sql BULK COLLECT INTO temp_details_list;
v_link_open := TRUE;
-- Print headers if data found
IF temp_details_list.COUNT > 0 THEN
-- Check the usage percentage and whether it meets the mail sending condition
v_actions_found := FALSE;
FOR i IN 1..temp_details_list.COUNT LOOP
v_total_mb := temp_details_list(i).total_mb;
v_tablespace_mb_used := temp_details_list(i).tablespace_mb_used;
v_tablespace_percentage_used := (v_tablespace_mb_used / v_total_mb) * 100;
IF v_tablespace_percentage_used >= v_used OR (v_total_mb - v_tablespace_mb_used) / v_total_mb * 100 <= 100 - v_used THEN
v_actions_found := TRUE;
EXIT;
END IF;
END LOOP;
-- HTML headers
v_html :=
v_html
|| '<h2>DBLINK: ' || v_db_link_name || '</h2>'
|| '<table border="1" cellpadding="5" cellspacing="0">'
|| '<tr>'
|| '<th>TABLESPACE</th>'
|| '<th>OS_USERNAME</th>'
|| '<th>SQL_TEXT</th>'
|| '<th>QUERY_MB_USED</th>'
|| '<th>TOTAL_MB</th>'
|| '<th>QUERY_PERCENTAGE_USED</th>'
|| '<th>TABLESPACE_PERCENTAGE_USED</th>'
|| '</tr>';
FOR i IN 1..temp_details_list.COUNT LOOP
v_query_mb_used := temp_details_list(i).query_mb_used;
v_query_percentage_used := (v_query_mb_used / v_total_mb) * 100;
v_html :=
v_html
|| '<tr>'
|| '<td>' || temp_details_list(i).tablespace || '</td>'
|| '<td>' || temp_details_list(i).os_username || '</td>'
|| '<td>' || temp_details_list(i).sql_text || '</td>'
|| '<td>' || TO_CHAR(temp_details_list(i).query_mb_used) || '</td>'
|| '<td>' || TO_CHAR(temp_details_list(i).total_mb) || '</td>'
|| '<td>' || TO_CHAR(v_query_percentage_used, 'FM9999990.000') || '%</td>'
|| '<td>' || TO_CHAR(v_tablespace_percentage_used, 'FM9999990.000') || '%</td>'
|| '</tr>';
END LOOP;
v_html := v_html || '</table>';
ELSE
v_html :=
v_html
|| '<h2>Detalles de DBLINK: ' || v_db_link_name || '</h2>'
|| '<p>TEMP not in use.</p>';
END IF;
COMMIT;
IF v_link_open THEN
EXECUTE IMMEDIATE 'BEGIN DBMS_SESSION.CLOSE_DATABASE_LINK('''||v_db_link_name||'''); END;';
v_link_open := FALSE;
END IF;
EXCEPTION
WHEN OTHERS THEN
IF v_link_open THEN
EXECUTE IMMEDIATE 'BEGIN DBMS_SESSION.CLOSE_DATABASE_LINK('''||v_db_link_name||'''); END;';
END IF;
DBMS_OUTPUT.PUT_LINE('Error processing DBLINK ' || v_db_link_name || ': ' || SQLERRM);
END;
END LOOP;
IF v_actions_found THEN
MONITORING_SCHEMA.PG_ENVIO_MAIL.entrega(
vg_from => '[email protected]',
vg_to => '[email protected]',
vg_asunto => 'TEMP Monitoring',
vg_cuerpo => v_html,
vg_firma => 'sender',
vg_cc => '[email protected]'
);
END IF;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line(DBMS_UTILITY.format_error_stack);
END;
/
SHOW ERRORS;
我设置了一个作业,当 v_used = 85% 的存储空间已满(任何 DBLINK)时,每 5 分钟执行一次发送邮件的过程,但我被告知,当它已满时,它不会发送电子邮件,或者至少会发送电子邮件需要时不捕获完整的表空间。我的问题是,主要查询正确吗?
WITH sort_usage AS (
SELECT
T.tablespace,
SUM(T.blocks * TBS.block_size) / 1024 / 1024 AS mb_used,
S.osuser,
Q.sql_text
FROM
v$sort_usage@'||v_db_link_name||' T
JOIN
v$session@'||v_db_link_name||' S ON T.session_addr = S.saddr
LEFT JOIN
v$sqlarea@'||v_db_link_name||' Q ON T.sqladdr = Q.address
JOIN
dba_tablespaces@'||v_db_link_name||' TBS ON T.tablespace = TBS.tablespace_name
GROUP BY
T.tablespace, S.osuser, Q.sql_text
),
tablespace_summary AS (
SELECT
A.tablespace_name AS tablespace,
SUM(A.used_blocks * D.block_size) / 1024 / 1024 AS mb_used,
SUM(D.mb_total) AS total_mb
FROM
v$sort_segment@'||v_db_link_name||' A
JOIN
(SELECT
B.name,
C.block_size,
SUM(C.bytes) / 1024 / 1024 AS mb_total
FROM
v$tablespace@'||v_db_link_name||' B
JOIN
v$tempfile@'||v_db_link_name||' C ON B.ts# = C.ts#
GROUP BY
B.name,
C.block_size) D ON A.tablespace_name = D.name
GROUP BY
A.tablespace_name
)
SELECT
ts.tablespace,
su.osuser,
su.sql_text,
su.mb_used AS query_mb_used,
ts.mb_used AS tablespace_mb_used,
ts.total_mb
FROM
tablespace_summary ts
JOIN
sort_usage su ON ts.tablespace = su.tablespace
ORDER BY
query_mb_used DESC'
或者我的方法遗漏了一些东西?
如果您有 RAC 数据库(多个主机安装数据库),则需要使用
gv$sort_segment
(或者,gv$sort_usage
)而不是单节点 v$
版本,该版本不会捕获正在消耗的数据通过您当前未连接的其他实例中的会话。这会低估所使用的数量。
其次,一旦纠正了这个问题,您还需要更改此行以使用
MAX
而不是 SUM
:
SUM(D.mb_total) AS total_mb
由于这是在排序段的级别,并且每个实例在每个临时空间中都可以有一个段,因此外部查询的粒度比内部查询的粒度低,因此
SUM
会乘以已经正确的表空间大小比应有的大得多。将 SUM
更改为 MAX
即可修复该问题。
一个潜在的问题是
gv$sort_usage
只会显示正在进行的SQL工作区——如果没有,你什么也得不到。这将消除您查询中的任何结果,因为您是在最后加入的。请记住,SQL 排序/哈希区域并不是唯一可以使用 temp 的区域。例如,全局临时表也可以,但它们不会显示在该视图中。
您还可以使用
gv$temp_extent_pool
。
为了有效,你确实需要每分钟执行一次。 5分钟间隔太远了——5分钟内就会消耗掉很多温度。如果它在本地运行而不是通过数据库链接运行也是最好的。