In meinem post über ILM storage tiering habe ich über ADO-Bedingungen geschrieben, die wir für Storage Tiering Policies angeben können, wenn Datensegmente in einen Read Only Tablespace verschoben werden. Andere Möglichkeit, Storage Tiering mit einer Bedingung zu versehen wäre eine benutzerdefinierte PL/SQL-Funktion, die einen Boolean-Wert zurückgibt. Wie funktioniert das? Und können wir auf die Heat Map Information zurückgreifen?
Leider gibt die Dokumentation auf den ersten Blick nicht so viel Information her:
ON
Use this clause to specify a PL/SQL function that returns a boolean value. For function_name, specify the name of the function. The policy will take effect when the function returns TRUE.
Ein gutes Beispiel ist im Blog von Uwe Hesse zu finden. Das Beispiel verwendet eine einfache Funktion im Sinne einer EIN/AUS-Logik. Wahrscheinlich war es auch der Grund, warum ich den entscheidenden Punkt lange übersehen hatte. I habe mich gefragt, ob es oft Sinn macht, eine benutzerdefinierte Bedingung zu definieren, die nicht in Kenntniss von Objekten ist, für die die Bedingung überprüft wird? Wenn nicht, wie übergeben wir die Informationen zu diesen Objekten? Und endlich habe ich den Parameter der Funktion gesehen (der gar nicht verwendet wurde)! Die Funktion akzeptiert OBJECT_ID als Parameter.
Seit dem war es wie mit dem Baader-Meinhof Phänomen (Frequency Illusion): ich habe auch ein anderes gutes Beispiel im MOS Doc ID 1967038.1 gefunden und sogar eine Dokumentationsseite, die – zwar nur im Beispiel – die Existenz des Parameters für die Policy-Funktion zeigt.
Das schauen wir uns an. Wir nehmen denselben Setup, wie für den vorherigen Beitrag über ILM Storage Tiering. Dieselbe Tabelle SCOTT.EMPLOYEE und derselbe Tablespace LOW_COST_STORE. We wollen, nachdem unsere benutzerdefinierte Bedingung als positiv evaluiert wurde, die Table zum neunen Tablespace verschieben. Wir versuchen mal die ADO-Bedingung „AFTER 10 DAY OF NO MODIFICATION“ nachzubauen.
Zuerst erstellen wir die Funktion zum Auswerten der gewünschten Bedingung. Mit der als Parameter übergebenen OBJECT_ID können wir aus der View USER_HEAT_MAP_SEGMENT die letzte Modifizierungszeit abfragen. Hier müssen wir den Wert vom ILM-Paramater POLICY_TIME überprüfen, damit wir mit dessen Änderung für Testzwecke umgehen können.
Nach dem Erstellen der Funktion, können wir bereits die Policy erstellen, die diese Funktion verwendet und die Informationen aus dem Data Dictionary anzeigen lassen. Beachten Sie, dass die Datenbank in dem Fall kein Wissen über unsere Bedingung hat (CONDITION_DAYS=0).
CREATE OR REPLACE FUNCTION f_custom (p_object_id number) RETURN BOOLEAN IS 2 v_time NUMBER; 3 v_days_no_modification NUMBER := 10; 4 v_ilm_parameter number; 5 BEGIN 6 SELECT sysdate-SEGMENT_WRITE_TIME 7 INTO v_time 8 FROM user_heat_map_segment 9 WHERE object_name = (SELECT object_name FROM user_objects WHERE object_id=p_object_id); 10 11 SELECT value 12 INTO v_ilm_parameter 13 FROM dba_ilmparameters WHERE name = 'POLICY TIME'; 14 15 IF v_ilm_parameter = 1 /*DBMS_ILM_ADMIN.POLICY_IN_SECONDS*/ THEN 16 v_time:=v_time*60*60*24; 17 END IF; 18 19 RETURN v_time > v_days_no_modification; 20 21 END; 22 / Function created. SCOTT> -- Storage tiering with user defined condition function SCOTT> ALTER TABLE scott.employee1 ILM ADD POLICY TIER TO low_cost_store ON f_custom; Table altered. SCOTT> ---------------------------------------------------------------------------------------------------- SCOTT> -- Show ILM policy information SCOTT> ---------------------------------------------------------------------------------------------------- SCOTT> SELECT policy_name, action_type, scope, tier_tablespace 2 , condition_type, condition_days, custom_function 3 FROM user_ilmdatamovementpolicies; POLICY_NAME ACTION_TYPE SCOPE TIER_TABLESPACE CONDITION_TYPE CONDITION_DAYS CUSTOM_FUNCTION ----------- ----------- ------- ---------------- ---------------------- -------------- ------------------ P261 STORAGE SEGMENT LOW_COST_STORE USER DEFINED 0 F_CUSTOM SCOTT> SCOTT> SELECT policy_name, object_name, object_type, enabled 2 FROM user_ilmobjects; POLICY_NAME OBJECT_NAME OBJECT_TYPE ENABLED ----------- -------------------- ------------------ ------- P261 EMPLOYEE1 TABLE YES
Jetzt stellen wir das Heat Map wieder für Testzwecke auf die Sekundeneinheiten um, aktualisieren die Tabelle und lassen die Heat Map Information wegschreiben. Die Abfrage von USER_HEAT_MAP_SEGMENT zeigt die letzte Änderungszeit WRITE_TIME für die Tabelle EMPLOYEE1. Wir führen ILM manuell aus und prüfen, wo die Table EMPLOYEE1 jetzt ist. Nichts passiert? Die Abfrage zeigt weiterhin den Tablespace USERS an. Ist aber auch richtig so – wir haben keine 10 Sekunden abgewartet. Die View USER_ILMEVALUATIONDETAILS bestätigt das: PRECONDITION NOT SATISFIED.
SCOTT> EXECUTE dbms_ilm_admin.customize_ilm(DBMS_ILM_ADMIN.POLICY_TIME, DBMS_ILM_ADMIN.ILM_POLICY_IN_SECONDS); PL/SQL procedure successfully completed. SCOTT> -- 2. set start date SCOTT> EXECUTE DBMS_ILM_ADMIN.SET_HEAT_MAP_START(sysdate -50); PL/SQL procedure successfully completed. SCOTT> SCOTT> UPDATE scott.employee1 e SET e.empno = e.empno+1; 3584 rows updated. SCOTT> commit; Commit complete. SCOTT> SCOTT> EXECUTE dbms_ilm.flush_all_segments; PL/SQL procedure successfully completed. SCOTT> SELECT object_name, to_char(segment_write_time,'DD-MON-YY hh24:mi:ss') write_time 2 FROM user_heat_map_segment 3 WHERE object_name ='EMPLOYEE1'; OBJECT_NAME WRITE_TIME -------------------- --------------------------- EMPLOYEE1 21-OCT-17 11:03:50 SCOTT> SCOTT> declare 2 v_executionid number; 3 begin 4 dbms_ilm.execute_ILM (ILM_SCOPE => dbms_ilm.SCOPE_SCHEMA, 5 execution_mode => dbms_ilm.ilm_execution_offline, 6 task_id => v_executionid); 7 end; 8 / PL/SQL procedure successfully completed. SCOTT> ---------------------------------------------------------------------------------------------------- SCOTT> -- Table EMPLOYEE1 was NOT moved to tablespace LOW_COST_STORE SCOTT> -------------------------------------------------------------------------------------------------- SCOTT> select tablespace_name, segment_name 2 from user_segments 3 where segment_name='EMPLOYEE1'; TABLESPACE_NAME SEGMENT_NAME ------------------------------ -------------------- USERS EMPLOYEE1 SCOTT> SCOTT> select task_id, object_name, selected_for_execution from user_ilmevaluationdetails; TASK_ID OBJECT_NAME SELECTED_FOR_EXECUTION ---------- -------------------- ------------------------------------------ 219 EMPLOYEE1 PRECONDITION NOT SATISFIED
Dann warten wir 10 Sekunden und wiederholen den Versuch. Jetzt wurde die Tabelle EMPLOYEE1 in den neuen Tablespace verschoben.
SCOTT> EXECUTE dbms_lock.sleep(10); PL/SQL procedure successfully completed. SCOTT> SCOTT> declare 2 v_executionid number; 3 begin 4 dbms_ilm.execute_ILM (ILM_SCOPE => dbms_ilm.SCOPE_SCHEMA, 5 execution_mode => dbms_ilm.ilm_execution_offline, 6 task_id => v_executionid); 7 end; 8 / PL/SQL procedure successfully completed. SCOTT> ---------------------------------------------------------------------------------------------------- SCOTT> -- Table EMPLOYEE1 is now moved to tablespace LOW_COST_STORE SCOTT> -------------------------------------------------------------------------------------------------- SCOTT> select tablespace_name, segment_name 2 from user_segments 3 where segment_name='EMPLOYEE1'; TABLESPACE_NAME SEGMENT_NAME ------------------------------ -------------------- LOW_COST_STORE EMPLOYEE1 SCOTT> SCOTT> SELECT task_id, to_char(start_time, 'dd/mm/yyyy hh24:mi:ss') as start_time, u.STATE 2 FROM user_ilmtasks u; TASK_ID START_TIME STATE ---------- ------------------- --------- 219 21/10/2017 11:03:52 COMPLETED 220 21/10/2017 11:04:13 COMPLETED SCOTT> SCOTT> select task_id, job_name, job_state, to_char(completion_time, 'dd/mm/yyyy hh24:mi:ss') completion 2 from user_ilmresults; TASK_ID JOB_NAME JOB_STATE COMPLETION ---------- --------------- ----------------------------------- ------------------------------ 220 ILMJOB4212 COMPLETED SUCCESSFULLY 21/10/2017 11:04:14 SCOTT> SCOTT> select task_id, object_name, selected_for_execution from user_ilmevaluationdetails; TASK_ID OBJECT_NAME SELECTED_FOR_EXECUTION ---------- -------------------- ------------------------------------------ 219 EMPLOYEE1 PRECONDITION NOT SATISFIED 220 EMPLOYEE1 SELECTED FOR EXECUTION
Es funktioniert also. Wenn man statt zu warten, bis der Schwellenwert beim Tablespace erreicht wird, die Storage Tiering Policies wie für Compression Tiering auch auf Heat Map Information basieren will, kann man es mit Hilfe benutzerdefinierter Funktionen erreichen. Leider verliert man dabei ein gutes Stück Transparenz und Selbstdokumentation, weil die Bedingungen nicht im Data Dictionary gespeichert werden.