一、問題展現
由於本人從事數據中心項目,數據中心有一個共享實例是對外提供數據的,6月11日發現數據庫報ora_12516錯誤,一般ORA-12516有兩個原因,一個是session數不夠
,另一個就是客戶端和服務端建立連接的時候頻繁鏈接數據庫,打開數據庫鏈接而不關閉導致的。
通過plsq工具查詢session,發行從6月9日到6月11日上午,有個用戶頻繁(每隔5分鍾就要鏈接一次)的鏈接數據庫,但是每次鏈接不能釋放,導致用戶該用戶鏈接超過900多,正於是電話溝通業務廠家,讓他們盡快排查關閉鏈接的功能(通過jdbc懷疑沒有關閉鏈接功能或者關閉失效),但是數據庫不能停止,只有自己想辦法(我不是dba,而是開發人員),想起以前給其他項目寫過oracle單機處理殺掉無效進程的過程,於是試試。
二、處理步驟
1、查看數據庫參數
1)查看當前數據庫的processes設置
SQL> show parameter processes
NAME TYPE VALUE
db_writer_processes integer 1
gcs_server_processes integer 0
job_queue_processes integer 10
log_archive_max_processes integer 2
processes integer 1200
SQL> show parameter sessions
NAME TYPE VALUE
java_soft_sessionspace_limit integer 0l
icense_max_sessions integer 0
license_sessions_warning integer 0
logmnr_max_persistent_sessions integer 1
sessions integer 1500
shared_server_sessions integer
一般按照經驗值,將processes數設置為1200,則sessions數必須為1.1*1200+5>=1325就行
數據庫參數應該是夠的,但是數據庫不能輕易的改動參數,估計dba來了只有改參數了,但是改參數需要走流程,需要時間還得重啟庫(正式環境重啟需要走流程),於是我覺得在等待業務廠家處理的同時,還是自己寫個kill過程吧,畢竟萬事不求人🐴。
2、編寫存儲過程(代碼為本人知識產權)
在包頭中定義:
CREATE OR REPLACE PACKAGE PKG_SYS IS
PROCEDURE PROC_KILL_INACTIVE_SESSIONS;
PROCEDURE SESSION_LOGS(P_SID IN NUMBER,
P_SERIAL IN NUMBER,
P_INST_ID IN NUMBER,
P_MODULE IN VARCHAR2,
P_STATUS IN VARCHAR2,
P_PROGRAM IN VARCHAR2,
P_MACHINE IN VARCHAR2,
P_LOGIN_TIME IN DATE,
P_MSG IN VARCHAR2,
P_OSUSER IN VARCHAR2);
END PKG_SYS;
/
在包體中編寫
CREATE OR REPLACE PACKAGE BODY PKG_SYS IS
/*-------------------------------------------------------------------------------------*/
/* */
/* (C) Copyright IEDS Corporation 2017 All Rights Reserved. */
/* */
/* 函數名稱 :PROC_KILL_INACTIVE_SESSIONS */
/* 功能說明 :殺無效進程 */
/* 參數說明 : */
/* 參數 (I/O) 類型 說明 */
/* 返回值說明 : */
/* 無 */
/* 詳細說明 : */
/* 維度頻率 :無 */
/* ORIGINAL : (1.0) 2017-05-25 CODED BY [IEDS] JINWEI */
/*-------------------------------------------------------------------------------------*/
PROCEDURE PROC_KILL_INACTIVE_SESSIONS AS
V_SID NUMBER;
V_SERIAL NUMBER;
V_INST_ID NUMBER;
V_MODULE VARCHAR2(100);
V_STATUS VARCHAR2(100);
V_PROGRAM VARCHAR2(100);
V_MACHINE VARCHAR2(100);
V_OSUSER VARCHAR2(100);
V_LOGIN_TIME DATE;
V_SQL VARCHAR2(1000);
V_PROC_MSG VARCHAR2(200);
VDAYS NUMBER;
CURSOR C is
select sid,
serial#,
inst_id,
module,
status,
program,
machine,
logon_time,
v.OSUSER
from gv$session v
where type != 'BACKGROUND'
and status IN ('INACTIVE' /*,'KILLED'*/)
and (sysdate - v.LOGON_TIME) > VDAYS
and username = 'share'
and v.PROGRAM like '%JDBC%';
BEGIN
--無效jdbc鏈接天數
VDAYS := 3;
--打開游標
open C;
loop
BEGIN
fetch C
into V_SID,
V_SERIAL,
V_INST_ID,
V_MODULE,
V_STATUS,
V_PROGRAM,
V_MACHINE,
V_LOGIN_TIME,
V_OSUSER;
exit when C%notfound;
V_SQL := 'alter system disconnect session ''' || V_SID || ',' ||
V_SERIAL || ''' immediate';
execute immediate V_SQL;
EXCEPTION
WHEN OTHERS THEN
V_PROC_MSG := 'disconnect SESSION_SID=' || V_SID || ' 失敗:';
V_PROC_MSG := V_PROC_MSG || 'SQLCODE(' || TO_CHAR(SQLCODE) ||
') SQLERRM(' || SUBSTR(SQLERRM, 1, 128) || ')';
--異常日志
SESSION_LOGS(V_SID,
V_SERIAL,
V_INST_ID,
V_MODULE,
V_STATUS,
V_PROGRAM,
V_MACHINE,
V_LOGIN_TIME,
V_PROC_MSG,
V_OSUSER);
END;
--正常日志
V_PROC_MSG := 'disconnect SESSION_SID=' || V_SID || '成功';
SESSION_LOGS(V_SID,
V_SERIAL,
V_INST_ID,
V_MODULE,
V_STATUS,
V_PROGRAM,
V_MACHINE,
V_LOGIN_TIME,
V_PROC_MSG,
V_OSUSER);
end loop;
close C;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END PROC_KILL_INACTIVE_SESSIONS;
--日志表
PROCEDURE SESSION_LOGS(P_SID IN NUMBER,
P_SERIAL IN NUMBER,
P_INST_ID IN NUMBER,
P_MODULE IN VARCHAR2,
P_STATUS IN VARCHAR2,
P_PROGRAM IN VARCHAR2,
P_MACHINE IN VARCHAR2,
P_LOGIN_TIME IN DATE,
P_MSG IN VARCHAR2,
P_OSUSER IN VARCHAR2) AS
-- 定義自動提交事務
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
--刪除3個月前日志
DELETE FROM JC_M_KILL_ORACLE_SESSION_LOGS T
WHERE T.NY <= TO_CHAR(ADD_MONTHS(SYSDATE, -3), 'YYYYMM');
--插入異常日志
INSERT INTO JC_M_KILL_ORACLE_SESSION_LOGS
(sid,
serial,
inst_id,
module,
status,
program,
machine,
logon_time,
SYSTIME,
NY,
MSG,
OSUSER)
SELECT P_SID,
P_SERIAL,
P_INST_ID,
P_MODULE,
P_STATUS,
P_PROGRAM,
P_MACHINE,
P_LOGIN_TIME,
SYSDATE,
TO_CHAR(SYSDATE, 'YYYYMM'),
SUBSTR(P_MSG, 1, 200),
P_OSUSER
FROM DUAL;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('SQLCODE(' || TO_CHAR(SQLCODE) || ') SQLERRM(' ||
SUBSTR(SQLERRM, 1, 128) || ')');
END SESSION_LOGS;
3、問題來了
1)問題1:
編譯過程 報視圖gv$session 在plsiq過程中不識別,缺乏權限。
原因:
Oracle為RAC集群機構,在單機可以,集群中gv$session和v$session都需要單獨授權
於是通過操作系統用戶root登錄
su - oracle
sqlplus as / sysdba
grant select on gv$session to A ;
但是 gv$session不能直接授權,需要授權執行視圖的同義詞才行;
grant select on g_v$session to A ;
原因:
我們常用的v$ 是v_$的同義詞,v_$是基於真正的視圖v$,而真正的v$視圖是在gv$的基礎上限制inst_id得到;
我們常用的gv$是gv_$的同義詞,gv_$基於真正的視圖gv$,而真正的gv$視圖基於系統表X$。
2)問題2:
繼續編譯,報錯disconnect SESSION無權限;
su - oracle
sqlplus as / sysdba
GRANT ALTER SYSTEM TO A;
到此存儲過程終於可執行了,但是還要增加定時自動執行JOB。
4、定時JOB
每天自動執行一次
begin
sys.dbms_scheduler.create_job(job_name => 'JOB_PROC_KILL_INACTIVE_SESSIONS',
job_type => 'PLSQL_BLOCK',
job_action => 'PROC_KILL_INACTIVE_SESSIONS;',
start_date => to_date('2016-01-02 18:00:00',
'yyyy-mm-dd hh24:mi:ss'),
repeat_interval => 'Freq=Day;Interval=1',
end_date => to_date(null),
job_class => 'DEFAULT_JOB_CLASS',
enabled => true,
auto_drop => false);
end;
/