Benutzer-Werkzeuge

Webseiten-Werkzeuge


dba:oracle_scheduler

Der Oracle Job Scheduler 10g/11g/12c/18c/19c

Erste Version 08/2018 - 10g, 11g, 12c, 18c, 19c

Der Oracle Jobs Scheduler in der Datenbank ist sehr mächtig und kann sehr umfangreich konfiguriert werden.

Mit dem Scheduler lassen sich komplexe Job Ketten aufbauen und komplexe Zeitpläne realisieren, das Filesystem überwachen etc.

Leider ist daher eine einfache Definition eines Standard Job im Umkehrschluss oft etwas kompliziert, da viele Parameter definiert werden können und/oder müssen.

Verstärkend kommt hinzu, das einzelne Elemente bei der Definition eines Jobs wiederum optional sind und mehrere Weg damit zum gleichen Ziel führen.

Siehe auch:


Übersicht über die Abhängigkeiten bei einer Definition eines Jobs:

 Übersicht über die Elemente der Oracle Job Steuerung per DBMS_SCHEDULER

Die einzelnen Elemente eines Jobs:

  • Job
    • Definiert den eigentlichen Job
    • Anlegen mit „DBMS_SCHEDULER.create_job“
    • Gehört einen Anwender/Schema der Datenbank
    • Kann die weiteren Elemente des Schedulers verwenden oder die Eigenschaften der Elemente werden direkt im Job hinterlegt
    • Besitzt immer ein Job Klasse, die Resource Gruppe und den DB Service definiert
    • Eimal Jobs löschen sich selbst wenn „Auto Drop“ = true
    • Ein Job kann eine Priority besitzen ( 1 bis 5 )
  • Program
    • Definiert WAS in einem Job ausgeführt werden soll
    • Dieses „Program“ kann von beliebig vielen Jobs verwendet werden
    • Anlegen mit „DBMS_SCHEDULER.create_program“
    • Gehört einen Anwender/Schema der Datenbank
    • Kann ein Externes Programm, ein PL/SQL Block oder eine Stored Procdure sein
    • Programm Argumente
      • Für diesen Aufruf können Argumente optional definiert werden
      • Anlegen mit „DBMS_SCHEDULER.define_program_argument“
  • Chain
    • Liste von Program oder Scheduler vom Type Event die nacheinander abgearbeitet werden
    • Diese „Chain“ kann von beliebig vielen Jobs verwendet werden
    • Können wiederum verschachtelt werden um komplexe Ketten abzubilden
    • Gehört einen Anwender/Schema der Datenbank
  • Job Class
    • Jeder Job besitze immer eine Job Klasse - meist den Default „DEFAULT_JOB_CLASS“
    • Definiert die für den laufenden Job verwendete Resource Consumer Group
      • Ist eine Resource Consumer Group UND ein Service definiert, wird die Resource Consumer Group über diese Einstellung auf der Job Class festgelegt und NICHT mehr über dem Mapping auf dem Service verändert!
    • Definiert den verwendeten DB Service - Ist dieser Service down oder nicht vorhanden wird KEIN Fehler geworfen ⇒ der Job mit der Klasse läuft einfach nicht!
    • Definiert den Log Level
  • Schedule
    • definiert die zeitliche Abarbeitung/Ausführung - Dieser Plan kann von beliebig vielen Jobs verwendet werden
    • Anlegen mit „DBMS_SCHEDULER.create_program“
    • Gehört einen Anwender/Schema der Datenbank
    • Dieser „Schedule“ kann von beliebig vielen Jobs verwendet werden
    • Zeitzone Angabe von erheblicher Bedeutung
    • Verschiedene Typen von Zeitplänen
      • Standard - Startzeit wählen, Intervall einstellen , bei Bedarf Endzeit einstellen
      • Event - AQ Event abholen und dann erst starten
      • Calender - Calender definieren
  • Window
    • Ein definiertes Zeitfenster, ist das Ende des Zeitfenster erreicht, kann je nach Bedarf der Job abgebrochen werden
    • Anlegen mit „DBMS_SCHEDULER.create_window“
    • Für ein Fenster kann ein Ressource Plan gewählt werden
    • Wird während eines offenen Zeitfensters die DB gestoppt, kann es passieren das diese Fenster nicht mehr geschlossen wird und damit keine Job mit diesem Fenster mehr startet!
    • Nur ein Window kann zur gleiche Zeit in der DB aktiv sein, ⇒ setzt den aktive Resourcen Plan für den Resource Manager!
      • Ist das Fenster aktiv wird der globale Resource Plan für die gesamte DB auf den Plan für diese Fenster gesetzt und der init.ora Parameter „resource_manager_plan“ wird vom Fenster gesetzt (zum Beispiel auf „SCHEDULER[0x196257]:GPI_LOW_LOAD“)
    • Über den dann aktiven Ressource Plan und der Consumer Group + Service Angabe in der Job Class wird der Job auf das Fenster gebunden
  • Window Group
    • Organisationsgruppe für alle Windows
    • Eine Gruppe kann ein- und ausgeschaltet werden
  • Global Attributes
    • Globale Attribute für die gesamte Job Umgebung wie die aktuelle Zeitzone

Globale Parameter für die Jobs

  • DEFAULT_TIMEZONE - Zeitzone für den Job
  • EMAIL_SENDER
  • EMAIL_SERVER
  • EMAIL_SERVER_CREDENTIAL
  • EMAIL_SERVER_ENCRYPTION
  • EVENT_EXPIRY_TIME
  • FILE_WATCHER_COUNT
  • LAST_OBSERVED_EVENT
  • LOG_HISTORY
  • MAX_JOB_SLAVE_PROCESSES

Die aktuelle Zeitzone für den Job

Zeitzone überwachen:

SELECT DBMS_SCHEDULER.STIME FROM DUAL;
 
STIME
----------------------------------------
10.12.14 19:28:51,178000000 +01:00

Zeitzone setzen:

BEGIN
  DBMS_SCHEDULER.set_scheduler_attribute(
     attribute => 'default_timezone'
   , VALUE     => 'EUROPE/BERLIN');
END;
/
 
SELECT DBMS_SCHEDULER.STIME FROM DUAL;
 
STIME
------------------------------------------------------
10.12.14 19:31:04,039000000 EUROPE/BERLIN

Jobs überwachen

Für das Überwachen kann dieses Script hilfreich sein: http://orapowershell.codeplex.com/SourceControl/latest#sql/jobs_sheduler.sql

Übersicht über die DBA Views bgzl. dem DBMS Scheduler:

Übersicht der DB Views um Scheduler Jobs zu überwachen

Monitoring aktiveren

Setzen über den die JOB Klasse
BEGIN
  DBMS_SCHEDULER.SET_ATTRIBUTE (
   'DEFAULT_JOB_CLASS', 'logging_level', DBMS_SCHEDULER.LOGGING_FAILED_RUNS);
END;

Job Logs automatisch lösen

Global:

BEGIN
DBMS_SCHEDULER.SET_SCHEDULER_ATTRIBUTE('log_history','90');
END;
/

Auf einer Job Klasse:

BEGIN
DBMS_SCHEDULER.SET_ATTRIBUTE('DEFAULT_JOB_CLASS','log_history','130');
END;

Job Logs per Hand löschen

Alles:

BEGIN
DBMS_SCHEDULER.PURGE_LOG();
END;
/

Nur alles was älter ist als 10 Tage und nur vom JOB LOG:

EXEC DBMS_SCHEDULER.PURGE_LOG(log_history => 10, which_log => 'JOB_LOG');

Fein Granuar:

EXEC DBMS_SCHEDULER.PURGE_LOG(log_history => 10, job_name => 'CLEAN_SQL_ERROR_LOG_TABLE, DEFAULT_JOB_CLASS');                      

Einen Job ohne weitere Elemente (alles "inline") anlegen

Einfachste Methode um einen Scheduler Job anzulegen:

BEGIN
   DBMS_SCHEDULER.create_job (
      job_name        => 'CLEAN_SQL_ERROR_LOG_TABLE'
    , job_type        => 'PLSQL_BLOCK'
    , job_action      => 'BEGIN system.deleteOraErrorTrigTab(15); END;'
    , start_date      => SYSTIMESTAMP
    , repeat_interval => 'freq=daily; byhour=13; byminute=0'
    , end_date        => NULL
    , enabled         => TRUE
    , comments        => 'Job to clean all lean Error Log older then xx days');
END;
/

Einen Lightwight Job ohne weitere Elemente anlegen

Ab 11g R1 kann ein Job „Lightweight“ angelegt werden, der Vorteil ist das weniger im Data Dictionary an Objekten angelegt werden muss.

Das erleichert das Anlegen, wenn viele einmalige Jobs angelegt werden müssen (Parameter job_style ⇒ 'LIGHTWEIGHT' !).

Allerdings muss dazu zuvor ein „Program“ definiert werden.

BEGIN
  DBMS_SCHEDULER.create_program (
      program_name        => 'CLEAN_SQL_ERROR_LOG_TABLE_PROG'
    , program_type        => 'STORED_PROCEDURE'
    , program_action      => 'system.deleteOraErrorTrigTab'
    , number_of_arguments => 1
    , enabled             => FALSE
    , comments            => 'Prog to clean Error Log older then xx days');
END;
/
 
BEGIN
  DBMS_SCHEDULER.define_program_argument (
      program_name      => 'CLEAN_SQL_ERROR_LOG_TABLE_PROG'
    , argument_name     => 'p_keepdays'
    , argument_position => 1
    , argument_type     => 'NUMBER'
    , default_value     => '15');
END;
/
BEGIN
  DBMS_SCHEDULER.enable (name => 'CLEAN_SQL_ERROR_LOG_TABLE_PROG');
END;
/
 
BEGIN
  DBMS_SCHEDULER.create_job (
     job_name         => 'CLEAN_SQL_ERRTABLE_LIGHT'
    , program_name    => 'CLEAN_SQL_ERROR_LOG_TABLE_PROG'
            , job_style       => 'LIGHTWEIGHT'
    , comments        =>  'Job to clean all lean Error Log older then xx days'
            , enabled         => true);
END;
/
 
-- nur im Job Log steht ein Eintrag!
 
select * 
  from dba_scheduler_job_log
where job_name = 'CLEAN_SQL_ERRTABLE_LIGHT'
;

Einen Job mit "Program" und "Schedule" anlegen

-- programm von vorherigen Beispiel Lightwight Job wird weiterverwendet
 
select *  
  from dba_scheduler_programs 
 where program_name = 'CLEAN_SQL_ERROR_LOG_TABLE_PROG'
/ 
 
 
-------------------------------------------------------------------------
-- Create Oracle Scheduler Time Plan
BEGIN
  DBMS_SCHEDULER.create_schedule (
     schedule_name   => 'DBA_TIMEPLAN'
    , start_date      => SYSTIMESTAMP
    , repeat_interval => 'freq=daily; byhour=13; byminute=0'
    , end_date        => NULL
    , comments        => 'Job time plan for DB Maintenance');
END;
/
 
select * 
  from dba_scheduler_schedules 
 where schedule_name = 'DBA_TIMEPLAN'
/ 
 
 
-------------------------------------------------------------------------
-- Create Scheduler Job
BEGIN
  DBMS_SCHEDULER.create_job (
     job_name         => 'CLEAN_SQL_ERROR_LOG_TABLE'
    , program_name    => 'CLEAN_SQL_ERROR_LOG_TABLE_PROG'
    , schedule_name   => 'DBA_TIMEPLAN'   
    , comments        =>  'Job to clean all lean Error Log older then xx days'
    , enabled         => false);
 
  DBMS_SCHEDULER.set_job_argument_value( job_name => 'CLEAN_SQL_ERROR_LOG_TABLE'
	                               , argument_name => 'p_keepdays'												
                                       , argument_value => '10'
	);         
 
 
  DBMS_SCHEDULER.enable (name => 'CLEAN_SQL_ERROR_LOG_TABLE');
 
END;
/ 
 
select * from dba_scheduler_jobs 
where job_name = 'CLEAN_SQL_ERROR_LOG_TABLE'
/

Alles wieder entfernen:

begin
  DBMS_SCHEDULER.drop_job      (job_name       => 'CLEAN_SQL_ERROR_LOG_TABLE');
  DBMS_SCHEDULER.drop_schedule (schedule_name  => 'DBA_TIMEPLAN');
  DBMS_SCHEDULER.drop_program  (program_name   => 'CLEAN_SQL_ERROR_LOG_TABLE_PROG');
end;
/  

RAC - Instance Stickiness - Job auf eine Instance binden

Lösung A) Über eine Job Klasse und einen Service
  • Einen Service im Cluster anlegen der NUR auf der gewünschten Instance läuft
  • Eine Job Klasse anlegen die diesen Service verwendet
  • Dem Job diese Job Klasse zuordnen
Lösung B) - Schalter Instance Stickiness => True

Wenn „Instance Stickiness“ auf „TRUE“ dann versucht die DB den Job auf der Instance wieder zu starten, auf der zu letzt der Job lief, ABER nur wenn diese Instance auch die Instance mit dem aktuelle geringsten Last ist! D.h. es kann sich in einem System mit stark wechselnder Last nicht darauf verlassen werden das der Job wirklich jedesmal auf der selben Instance startet!

Lösung C) Job auf eine Instance binden
# stetzen
begin  
  dbms_scheduler.set_attribute( name => 'GPI.DO_MY_JOB' 
                               , attribute=>'INSTANCE_ID'
                               , value=> 3
  );  
end;  
/ 
 
#prüfen mit:
 
set serveroutput on
 
declare
 v_out varchar2(255);
begin
  dbms_scheduler.get_attribute( name     => 'GPI.DO_MY_JOB' 
                              , attribute=>'INSTANCE_ID'
                              , value=> v_out
   );  
 
  dbms_output.put_line('-- Info   INSTANCE_ID Attribute::'||v_out);
end; 
/ 
 
#wieder auf null stetzen:
 
begin
  dbms_scheduler.set_attribute_null ( name       => 'GPI.DO_MY_JOB' 
                                   ,attribute  => 'INSTANCE_ID'
  );
end;
/

Scheduler Calendaring Syntax

Bei DBMS_SCHEDULER.create_schedule kann über ein Zeitintervall angegeben werden.

Die Syntax ist recht komplex ⇒http://docs.oracle.com/cd/E11882_01/appdev.112/e40758/d_sched.htm#ARPLS72273 ( nach repeat_interval suchen), für 12c siehe https://docs.oracle.com/en/database/oracle/oracle-database/12.2/admin/scheduling-jobs-with-oracle-scheduler.html#GUID-10B1E444-8330-4EC9-85F8-9428D749F7D5

Beispiele:

RegelBedeutung
FREQ=hourly;BYMINUTE=0stündlich aufrufen
FREQ=MINUTELY; INTERVAL=15alle 15 Minuten aufrufen
FREQ=WEEKLY; BYDAY=MONJeden Montag aufrufen
FREQ=WEEKLY; BYDAY=Mon;BYHOUR=2;BYMINUTE=30;BYSECOND=0 Jeden Montag um 2:30:00 aufrufen

Der Calender Ausdruck kann mit DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING getestet werden.

DECLARE	
	v_next_run_date TIMESTAMP;
	v_start_date    TIMESTAMP:=systimestamp;  
        v_return_date_after TIMESTAMP
BEGIN
  FOR i IN 1 ..10
  loop
  DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING(  
		  calendar_string   => 'FREQ=MINUTELY; INTERVAL=15'
		, start_date        => v_start_date
		, return_date_after => v_return_date_after 
		, next_run_date     => v_next_run_date);
 
	DBMS_OUTPUT.PUT_LINE('-- Info actual date :: '||to_char(v_start_date,'dd.mm.yyyy hh24:mi')||
	                     '   --> next_run_date:: '||to_char(v_next_run_date,'dd.mm.yyyy hh24:mi')
	);
         v_return_date_after := v_next_run_date;
  END loop;
END;
/
 
-- Info actual date :: 16.01.2015 19:19   --> next_run_date:: 16.01.2015 19:34
 
-- als Funktion
 
CREATE OR REPLACE FUNCTION getNextRunDate(p_calendar_string      varchar2
                                         ,p_start_date           TIMESTAMP WITH TIME ZONE DEFAULT systimestamp
                                         ,p_return_date_after    TIMESTAMP WITH TIME ZONE DEFAULT systimestamp)
   RETURN DATE
IS
   v_next_run_date   TIMESTAMP WITH TIME ZONE;
BEGIN
   dbms_scheduler.EVALUATE_CALENDAR_STRING(calendar_string     => p_calendar_string
                                          ,start_date          => p_start_date
                                          ,return_date_after   => p_return_date_after
                                          ,next_run_date       => v_next_run_date);
 
   RETURN v_next_run_date;
END;
/
 
-- abfragen über alle Jobs wann die Jobs das nächste mal und das übernächste mal laufen würden:
 
SELECT js.job_name
      , JS.REPEAT_INTERVAL
      , js.next_run_date
      , getNextRunDate(JS.REPEAT_INTERVAL,js.next_run_date,js.next_run_date) AS over_next_run_date
      , js.state||' - '||js.job_action  AS job_info     
    FROM dba_scheduler_jobs js
       , dba_objects o
   WHERE     js.owner = o.owner(+)
         AND js.job_name = o.OBJECT_NAME(+)
         AND js.owner != 'SYS'
/         

Nachträglich ändern:

BEGIN
DBMS_SCHEDULER.SET_ATTRIBUTE (
     name       => 'CHECK_LONG_RUNNING_TIMEPLAN'
   , attribute  => 'repeat_interval'
   , VALUE      =>  'FREQ=MINUTELY; INTERVAL=15');
END;
/
 
 
SELECT * 
  FROM dba_scheduler_schedules 
 WHERE schedule_name = 'DBA_TIMEPLAN'
/ 

Window Funktionalität nützen

Ein Scheduler Window legt den aktiven Resource Plan für den Zeitraum, in dem das Fenster „offen“ ist, fest.

D.h. ein per init.ora definiert globaler Resourceplan wird von diesem Scheduler Window überschrieben!

Vorteil:

  • Damit lassen sich mehrere Resouce Pläne zeitlich organisieren

Nachteil:

  • Der eigentlich gültige Plan kann überschrieben werden, ohne das man sich dessen so einfach bewust ist!

siehe auch ⇒ Den Oracle Resource Manager ab 11g verwenden

Über den Ressource Plan im Windows und der Consumer Group + Service Angabe in der Job Class wird dann der Job auf das Window gebunden und entsprechend in der Gruppe des Resource Plans priorisiert.

Übersicht:

 Oracle Scheduler Window and Resource Manager

Siehe Statement dazu im Orginal Doku ⇒ Job Classes

Das der Scheduler den Plan gesetzt hat, kann am init.ora Parameter „resource_manager_plan“ erkannt werden, hier steht dann ein „SCHEDULER[0x196257]:GPI_LOW_LOAD“ im aktuellen Wert!

Die Bedeutung des 0x196257 ist leider nuklar, gibt kein DB Objekt mit der ID, und die ID in den Log Tabellen ist es auch nicht. Am suchen…

Überwachen mit:

Übersicht über die beteiligten Views:

 DB Views Window Funktionalität

Zu welchen Plan ist ein Job Fenster zugeordnet:

COLUMN job_class_name    format a25
COLUMN job_class_service format a25
COLUMN resource_plan     format a28
COLUMN consumer_group    format a25
COLUMN job_window        format a20
 
 
SELECT jc.job_class_name
     , jc.service AS job_class_service 
     , cg.consumer_group
     , pd.plan     AS resource_plan
     , sw.window_name AS job_window
  FROM dba_scheduler_job_classes jc  
      , dba_rsrc_consumer_groups cg
      , dba_rsrc_plan_directives pd
      , dba_scheduler_windows sw
      , DBA_RSRC_GROUP_MAPPINGS gm
  WHERE jc.resource_consumer_group = cg.consumer_group                    
    AND cg.consumer_group=pd.group_or_subplan (+) 
    AND sw.resource_plan(+)=pd.group_or_subplan 
 ORDER BY 1
/

Probleme

ORA-27486: Nicht ausreichende Berechtigungen

Problem:

ORA-27486: Nicht ausreichende Berechtigungen
ORA-06512: IN "SYS.DBMS_ISCHED", Zeile 168
ORA-06512: IN "SYS.DBMS_SCHEDULER", Zeile 288
ORA-06512: IN Zeile 2

Lösung:

GRANT CREATE job TO <SCHEMA>;

ORA-27476: "%" does not exist Check the log/trace file for more Details

Eine Job Klasse muss den entsprechenden User „gegranted“ werden!

sys.dbms_scheduler.create_job( 
...
job_class => 'MY_ADMIN_CLASS'
...
 
...
 
ORA-27476: "MY_ADMIN_CLASS" does NOT exist CHECK the log/trace file FOR more Details
 
 
GRANT EXECUTE  ON SYS.MY_ADMIN_CLASS TO GPI;

Einen Job wieder entfernen

Über den <Schema Owner>.<Job Name> kann mit der Routine dbms_scheduler.drop_job ein Job entfernt werden, mit force⇒true wird der Job sofort gestoppt und entfernt, falls false erst nach dem nächsten Lauf.

Beispiel:

BEGIN
 dbms_scheduler.drop_job (job_name => 'BGJOBUSER.SYMON', force=>TRUE);
END;
/

Auswertung des Job Logs sehr langsam

Evlt. sind es einfach zuviele Einträge im Log, siehe ⇒ How To Purge DBA_SCHEDULER_JOB_LOG and DBA_SCHEDULER_WINDOW_LOG (Doc ID 443364.1)

Hierzu auch folgende Anmerkungen beachten ⇒ DBMS_SCHEDULER Job Log Lösch-Verhalten - Löschen optimieren


Job Überwachen

Hier ein SQL um alle Jobs mit Ihren Laufzeiten und den letzten Fehler aus den Log zu überwachen:

WITH jobs AS (
SELECT  js.owner
       ,  js.job_name
       ,  decode (js.state, 'SHUD', 'SCHEDULED', 'DIS' , 'DISABLED', 'RUN', 'RUNNING',   js.state) AS state
       ,  js.run_count
       ,  js.failure_count
       ,  to_char (js.last_start_date, 'dd.mm hh24:mi') AS last_start_date
       ,  to_char (js.next_run_date, 'dd.mm hh24:mi')   AS next_run_date
       , LAST_RUN_DURATION
    FROM dba_scheduler_jobs js
)
, 
last_log_date AS (
SELECT owner
    , job_name 
    , MAX(log_date) AS log_date
 FROM dba_scheduler_job_log 
GROUP BY  owner
    , job_name
    )
, last_logs AS (
  SELECT l.* 
    FROM dba_scheduler_job_log l  
INNER JOIN last_log_date ld 
  ON (    ld.owner=l.owner 
      AND  ld.job_name=l.job_name
      AND ld.log_date=l.log_date
    )
 
)
 
SELECT j.OWNER
    , j.JOB_NAME
    , j.STATE
    , j.RUN_COUNT
    , j.FAILURE_COUNT
    , round( EXTRACT( SECOND FROM j.LAST_RUN_DURATION ) 
          + EXTRACT( MINUTE FROM j.LAST_RUN_DURATION ) * 60 
          + EXTRACT( HOUR FROM j.LAST_RUN_DURATION ) * 60 * 60 
          + EXTRACT( DAY FROM j.LAST_RUN_DURATION ) * 60 * 60 * 24
            ,2) AS LAST_RUN_DURATION_SEC
    , j.LAST_START_DATE
    , j.NEXT_RUN_DATE
    , to_char(l.LOG_DATE, 'dd.mm hh24:mi') AS last_log_date
    , nvl(l.STATUS,'NO_LOG_FOR_THIS_JOB') AS  last_log_status
  FROM jobs j 
 LEFT JOIN last_logs l
   ON  (j.owner=l.owner AND  j.job_name=l.job_name)
WHERE 1=1
 -- only active  Jobs
 AND state != 'DISABLED'
ORDER BY j.owner,j.job_name
;

Quellen zum Thema

Diese Website verwendet Cookies. Durch die Nutzung der Website stimmen Sie dem Speichern von Cookies auf Ihrem Computer zu. Außerdem bestätigen Sie, dass Sie unsere Datenschutzbestimmungen gelesen und verstanden haben. Wenn Sie nicht einverstanden sind, verlassen Sie die Website.Weitere Information
dba/oracle_scheduler.txt · Zuletzt geändert: 2023/03/08 13:54 von gpipperr

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki