Monitor/sample values from gv$SesStat in history: monitor transaction count per session in history
In current version of Oracle database there’s no way to breakdown historic statistic values to sessions if session has already terminated.
Some times this information is strongly needed to identify bottlenecks.
Actual situation was:
– massive number of transactions which caused massive small write I/O reaching limits of physical disks
– to identify root cause identification fo processes/sessions was needed
– consideration can only be done in history with time gap of some hours, problematic sessions have been terminated for long time
– massive number of transactions which caused massive small write I/O reaching limits of physical disks
– to identify root cause identification fo processes/sessions was needed
– consideration can only be done in history with time gap of some hours, problematic sessions have been terminated for long time
Solution:
Create sampling process with minimal resource impact to CPU and I/O with result storage in database table.
Create sampling process with minimal resource impact to CPU and I/O with result storage in database table.
Preparation
Create two tables, one for result storage and one for abortion of sampling process:
Create two tables, one for result storage and one for abortion of sampling process:
CREATE TABLE SessMon_Semaphore (Name VARCHAR2(30)); COMMENT ON TABLE SessMon_Semaphore IS 'Control abortion of sampling process'; COMMENT ON COLUMN SessMon_Semaphore.Name IS 'Record identifier to store record lock on it'; CREATE TABLE SessMon_Log ( SampleTime DATE, StartTime DATE, EndTime DATE, Inst_ID NUMBER, SID NUMBER, Serial# NUMBER, Statistic# NUMBER, Value NUMBER ); COMMENT ON TABLE SessMon_Log IS 'Logging Table for session statistics'; COMMENT ON COLUMN SessMon_Log.SampleTime IS 'Timestamp of storage of sample record in table (end of sample cycle)'; COMMENT ON COLUMN SessMon_Log.StartTime IS 'First occurrence of session within sample cycle'; COMMENT ON COLUMN SessMon_Log.EndTime IS 'Last occurrence of session within sample cycle'; COMMENT ON COLUMN SessMon_Log.Inst_ID IS 'Instance number (allows link to active session history)'; COMMENT ON COLUMN SessMon_Log.SID IS 'Session identifier (allows link to active session history)'; COMMENT ON COLUMN SessMon_Log.Serial# IS 'Session serial number (allows link to active session history)'; COMMENT ON COLUMN SessMon_Log.Statistic# IS 'Statistic number from V$StatName'; COMMENT ON COLUMN SessMon_Log.Value IS 'Delta between last and current sample for value from GV$SesStat';
Sampling statistic results
Execute anonymous PL/SQL block which samples data in memory and periodically stores result in database table.
This requires execute grant on DBMS_LOCK for executor to reduce CPU consumption.
Execute anonymous PL/SQL block which samples data in memory and periodically stores result in database table.
This requires execute grant on DBMS_LOCK for executor to reduce CPU consumption.
DECLARE SEMAPHORE CONSTANT VARCHAR2(30) := 'Session Monitoring'; SAMPLE_CYCLE CONSTANT NUMBER := 10; -- Seconds between sampling in memory SAVE_CYCLE CONSTANT NUMBER := 900; -- Seconds between save to table on disk STAT_NAME CONSTANT VARCHAR2(30) := 'user commits'; -- Statistic name to sample according to v$StatName MIN_VALUE CONSTANT NUMBER := 1000; -- Minimum value in GV$SesStat to include in sample Loops_per_SaveCycle NUMBER := SAVE_CYCLE/SAMPLE_CYCLE; Loop_Count NUMBER := 0; StatNo_User_Commits NUMBER; First_Cycle BOOLEAN := true; -- First cycle should not be saved because of missing predecessor v_Count NUMBER; Begin_Memory_Cycle DATE := SYSDATE; -- Remember begin of cycle as timestamp of last snapshot TYPE Sample_Type IS RECORD ( Inst_ID SessMon_Log.Inst_ID%TYPE, SID SessMon_Log.SID%TYPE, Serial# SessMon_Log.Serial#%TYPE, Statistic# SessMon_Log.Statistic#%TYPE, StartTime SessMon_Log.StartTime%TYPE, EndTime SessMon_Log.EndTime%TYPE, Value SessMon_Log.Value%TYPE ); TYPE Sample_Table_Type IS TABLE OF Sample_Type INDEX BY VARCHAR2(30); Sample_Table Sample_Table_Type; Prev_Sample_Table Sample_Table_Type; -- Memory table with last saved values PROCEDURE LockSem IS Resource_Busy EXCEPTION; pragma Exception_Init(Resource_Busy, -54); v_Dummy SessMon_Semaphore.Name%TYPE; BEGIN BEGIN -- Try to aquire lock (don't wait): SELECT Name INTO v_Dummy FROM SessMon_Semaphore WHERE Name=SEMAPHORE FOR UPDATE NOWAIT; EXCEPTION WHEN Resource_Busy THEN -- another similar process tries to stop working RAISE_APPLICATION_ERROR(-20999, 'Process stopped because semaphore is locked by another process'); END; END LockSem; PROCEDURE Sample_Records_in_Memory IS v_Index VARCHAR2(30); Sample_Rec Sample_Type; BEGIN FOR Rec IN (SELECT t.Inst_ID, t.SID, s.Serial#, t.Statistic#, t.Value FROM GV$SesStat t JOIN GV$Session s ON s.Inst_ID = t.Inst_ID AND S.SID = t.SID WHERE t.Statistic#=StatNo_User_Commits AND t.Value > MIN_VALUE ) LOOP v_Index := Rec.Inst_ID||':'||Rec.SID||':'||Rec.Serial#||':'||Rec.Statistic#; IF Sample_Table.EXISTS(v_Index) THEN Sample_Rec := Sample_Table(v_Index); ELSE Sample_Rec.Inst_ID := Rec.Inst_ID; Sample_Rec.SID := Rec.SID; Sample_Rec.Serial# := Rec.Serial#; Sample_Rec.Statistic# := Rec.Statistic#; Sample_Rec.StartTime := Begin_Memory_Cycle; END IF; Sample_Rec.EndTime := SYSDATE; Sample_Rec.Value := Rec.Value; Sample_Table(v_Index) := Sample_Rec; END LOOP; Begin_Memory_Cycle := SYSDATE; -- Begin for next cycle END Sample_Records_in_Memory; PROCEDURE Save_Sample_Records IS v_Index VARCHAR2(30); Sample_Rec Sample_Type; BEGIN v_Index := Sample_Table.FIRST; WHILE v_Index IS NOT NULL LOOP Sample_Rec := Sample_Table(v_Index); IF Prev_Sample_Table.EXISTS(v_Index) THEN Sample_Rec.Value := Sample_Rec.Value - Prev_Sample_Table(v_Index).Value; -- Reduce value to delta between last saved cycle an now END IF; IF Sample_Rec.Value != 0 AND NOT First_Cycle THEN -- Suppress saving of 0-values INSERT INTO SessMon_Log (SampleTime, StartTime, EndTime, Inst_ID, SID, Serial#, Statistic#, Value) VALUES (SYSDATE, Sample_Rec.StartTime, Sample_Rec.EndTime, Sample_Rec.Inst_ID, Sample_Rec.SID, Sample_Rec.Serial#, Sample_Rec.Statistic#, Sample_Rec.Value); END IF; v_Index := Sample_Table.NEXT(v_Index); END LOOP; Prev_Sample_Table := Sample_Table; Sample_Table.DELETE; First_Cycle := false; END Save_Sample_Records; BEGIN SELECT COUNT(*) INTO v_Count FROM SessMon_Semaphore WHERE Name = SEMAPHORE; IF v_Count = 0 THEN INSERT INTO SessMon_Semaphore (Name) VALUES (SEMAPHORE); -- create Semaphore-record if not exists END IF; COMMIT; LockSem; SELECT Statistic# INTO StatNo_User_Commits FROM v$StatName WHERE Name = STAT_NAME; -- what statistic to sample LOOP Sample_Records_in_Memory; Loop_Count := Loop_Count + 1; IF Loop_Count >= Loops_per_SaveCycle THEN Save_Sample_Records; COMMIT; -- Free Semaphore, make samples visible to others LockSem; -- Cancel processing of semaphore is locked Loop_Count := 0; END IF; DBMS_LOCK.Sleep(SAMPLE_CYCLE); END LOOP; END; /
Finish sampling, analysis of results
Stop anonymous PL/SQL sampler by locking semaphore records for SAVE_CYCLE seconds until PL/SQL-Job terminates:
Stop anonymous PL/SQL sampler by locking semaphore records for SAVE_CYCLE seconds until PL/SQL-Job terminates:
select * from SessMon_Semaphore for update;
Find sessions in history with most resource consumption (user commits) enriched with context info from active session history:
SELECT l.*, (SELECT 'Module="'||MIN(h.Module)||'" Action="'||MIN(Action)||'" User_ID='||MIN(h.User_ID)|| ' Program="'||MIN(h.Program)||'" Machine="'||MIN(h.Machine)||'"' FROM DBA_Hist_Active_Sess_History h WHERE h.DBID = (SELECT DBID FROM v$Database) AND h.Snap_ID >= (SELECT MIN(Snap_ID) FROM DBA_Hist_Snapshot ss WHERE ss.Instance_Number = l.Inst_ID AND ss.End_Interval_Time >= l.StartTime) AND h.Snap_ID <= (SELECT MAX(Snap_ID) FROM DBA_Hist_Snapshot ss WHERE ss.Instance_Number = l.Inst_ID AND ss.Begin_Interval_Time <= l.EndTime) AND h.Instance_Number = l.Inst_ID AND h.Session_ID = l.SID AND h.Session_Serial# = l.Serial# ) ASH_Info FROM (SELECT Inst_ID, SID, Serial#, MIN(StartTime) StartTime, MAX(EndTime) EndTime, SUM(Value) Value FROM SessMon_Log GROUP BY Inst_ID, SID, Serial# ) l ORDER BY Value DESC ;
Comments
Post a Comment