SQL tuning in Oracle 11gR2

Oracle utilizza un ottimizzatore SQL molto sofisticato e potente. Questa breve paginetta riporta alcune indicazioni per sfruttarlo al meglio.

Avevo gia' scritto alcuni documenti sulle novita' dell'ottimizzatore Oracle [NdE novita' della versione 7.3!] e sui principali strumenti di verifica delle prestazioni. Anche se i concetti di base erano sempre gli stessi, sono anche passati quasi 20 anni quindi ho ritenuto opportuno scrivere questo aggiornamento allineato alla versione 11g R2!

Optimizer

L'SQL e' un linguaggio di quarta generazione, ovvero consente di dire cosa si vuole ma non come trovarlo. Gli statement SQL vengono parsificati (che e' una semplice verifica sintattica e sull'accesso ai dati) e quindi vengono analizzati dall'optimizer. L'optimizer ha il compito di decidere quale sia i modo migliore per estrarre i dati richiesti.
L'optimizer Oracle e' cost based, ovvero utilizza statistiche sofisticate sulle tabelle e sugli indici, ed elaborare un plan (piano di esecuzione) ottimizzato per l'SQL utilizzato.

Tutto questo vale in modo praticamente identico da oltre 20 anni, dalla versione 7 di Oracle all'attuale 12c. Ma in realta' di cose in vent'anni ne sono cambiate parecchie.
Per raccontarle tutte servirebbe un'enciclopedia... quindi raccontero' solo il poco che mi interessa e che conosco!

EXPLAIN PLAN, TKPROF, HINT, ANALYZE, ...

Nel bagaglio di esperienze di un DBA Oracle questi tool sono sempre presenti: EXPLAIN PLAN, TKPROF, HINT, ANALYZE!

L'explain plan lo diamo per scontato:

SYS@demo > explain plan for select 1 from dual;

Explained.

SYS@demo > SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1388734953

-----------------------------------------------------------------
| Id  | Operation	 | Name | Rows	| Cost (%CPU)| Time	|
-----------------------------------------------------------------
|   0 | SELECT STATEMENT |	|     1 |     2   (0)| 00:00:01 |
|   1 |  FAST DUAL	 |	|     1 |     2   (0)| 00:00:01 |
-----------------------------------------------------------------


SYSTEM  = optimizer_features_enable='11.2.0.3'
SESSION = alter session set optimizer_features_enable='11.2.0.3';
SQL     = select /*+ optimizer_features_enable('9.2.0') */

Sul TKPROF non ci sono novita' di rilievo...

Gli HINT sono sempre di piu'. Ovviamente sono una feature non standard di Oracle... ma anche altri RDBMS li stanno imitando.

L'Enterprise Manager fornisce un fracco di informazioni con un'interfaccia grafica semplice. Occorre solo fare attenzione al licensing! [NdA eventualmente impostare il parametro CONTROL_MANAGEMENT_PACK_ACCESS]

I possibili interventi sull'ottimizzatore sono innumerevoli:

select optimizer_feature_enable, count(*)
from v$system_fix_control
group by optimizer_feature_enable 

OPTIMIZER_FEATURE_ENABLE    COUNT(*)
------------------------- ----------
8.0.0				  85
8.1.6				   1
8.1.7				   2
9.2.0				   8
9.2.0.8 			   3
10.1.0				   3
10.1.0.3			   1
10.1.0.4			   1
10.1.0.5			   2
10.2.0.1			   9
10.2.0.2			  12
10.2.0.3			  11
10.2.0.4			  73
10.2.0.5			 111
11.1.0.6			  41
11.1.0.7			  19
11.2.0.1			  68
11.2.0.2			 107
11.2.0.3			  82
11.2.0.4			 141
				  65

Per tracciare il comportamento di uno statement SQL sono possibili diverse alternative a seconda delle versioni come ben descritto in questa paginetta di oracle-base (EVENT 10046) e nella nota Oracle How to Obtain Tracing of Optimizer Computations [ID 225598.1] (EVENT 10053).

Da ultimo, ma non per importanza, e' necessario ricordare l'ANALYZE: quanti problemi di tuning ha risolto il semplice lancio delle statistiche!

SQLT

SQLT (SQLTXPLAIN) e' a mio avviso lo strumento migliore per analizzare gli statement SQL. E' un tool sviluppato individualmente e poi adottato ufficialmente da Oracle. L'installazione e la configurazione sono banali e descritte in modo completo nella documentazione ufficiale [NdA DocId 215187.1].

L'analisi che SQLT effettua e' molto completa e consente di disporre di tutti gli elementi per ottimizzare una query. SQLT puo' essere lanciato con script diversi, con un grado decrescente di funzionalita', a seconda che lo statement SQL sia gia' stato eseguito o meno:

START sqltxtract.sql [SQL_ID]|[HASH_VALUE] [sqltxplain_password]
START sqltxecute.sql script [sqltxplain_password]
START sqltxplain.sql script sqltxplain_password
Da notare l'utility coe_xfr_profile.sql che genera ed importa l'SQL Profile scelto tra quelli analizzati. E' anche possibile utilizzare alcuni trucchi come modificare il force_match o analizzare uno statement con HINT e scambiare i profili... potentissimo!

Un'ultima nota. L'utilizzo di SQLT Profile non richiede le Option DIAGNOSTICS e TUNING pack ma, se assenti, opera in modalita' molto, molto ridotta. coe_xfr_profile richiede il TUNING pack.

Gestione SQL Profile

Il tuning di uno statement SQL puo' richiedere molto tempo e richiede una notevole esperienza... Ma seguendo il seguente esempio e' possibile automatizzare le attivita' di tuning utilizzando un tuning task:

spool analisi_tuning
set lines 180
set long 1000000
SET LONGCHUNKSIZE 1000000
variable report_out clob;
declare
stmt_task VARCHAR2(40);
begin
stmt_task := DBMS_SQLTUNE.CREATE_TUNING_TASK(sql_id => 'xxxyyyzzz');
DBMS_OUTPUT.put_line('task_id: ' || stmt_task );
DBMS_SQLTUNE.EXECUTE_TUNING_TASK(task_name => stmt_task );
SELECT DBMS_SQLTUNE.REPORT_TUNING_TASK( stmt_task ) into :report_out FROM dual;
end;
/
print report_out;
spool off

In pratica basta disporre dell'SQL Id dello statement per far provare ad Oracle. Lo script effettua il tuning dello statement e restituisce un report con le diverse alternative di plan.
Le indicazioni possono essere di diverso tipo: raccogliere statistiche, creare indici, applicare un profilo per far utilizzare un execution plan differente alla query, ... e debbono essere interpretate con attenzione.
Se si ritiene valida una delle indicazioni emerse basta accettarla come indicato nel report. Ad esempio:

begin
DBMS_SQLTUNE.ACCEPT_SQL_PROFILE(task_name => 'TASK_6969', task_owner => 'SYSTEM', replace => TRUE);
end;
/
Da questo momento e' attivo un nuovo SQL Profile che guida l'ottimizzatore per lo statement analizzato. La verifica dei profile presenti si effettua con:
select * from dba_sql_profiles;

Per disabilitare un profilo si utilizza:

begin
DBMS_SQLTUNE.ALTER_SQL_PROFILE('SQL_PROFILE','STATUS','DISABLED');
end;
/
Per controllare quali statement utilizzano un profile:
set lines 132
column sql_text format a130
select sql_id, child_number, plan_hash_value, sql_profile, EXECUTIONS, ELAPSED_TIME, BUFFER_GETS, sql_text
  from v$sql 
 where sql_profile is not null;

Un'ultima nota. L'utilizzo degli SQL Profile richiede le Option DIAGNOSTICS e TUNING pack.

SPM Baseline

alter system set OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES=true sid='*';

spool plan_table
SELECT PLAN_TABLE_OUTPUT
FROM   V$SQL s, DBA_SQL_PLAN_BASELINES b, 
       TABLE(
         DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE(b.sql_handle,b.plan_name,'basic') 
       ) t
WHERE  s.EXACT_MATCHING_SIGNATURE=b.SIGNATURE
AND    b.PLAN_NAME=s.SQL_PLAN_BASELINE
AND    s.

alter session set current_schema=them;

L'utilizzo di una baseline SPM richiede l'Edition Enterprise ma non e' necessaria nessuna Option.

DBMS_XPLAN

DECLARE
cl_sql_text CLOB;
BEGIN
SELECT sql_text INTO cl_sql_text from gv$sqlarea where sql_text like 'SELECT name FROM sql_prof_sample_1 t1 JOIN sql_prof_sample_2 t2 ON t1.type_id = t2.type_id WHERE t2.type_name%';
DBMS_SQLTUNE.IMPORT_SQL_PROFILE(sql_text => cl_sql_text, profile => sqlprof_attr('INDEX(@"SEL$1" "T1"@"SEL$1" SQL_PROF_SAMPLE_1_I)'), name => 'SQL_PROF_SAMPLE', force_match => TRUE);
end;
/

v$sql v$sqlarea v$sqlstats

Le informazioni di dettaglio sugli statement SQL si possono raccogliere da viste diverse.

V$SQL ha una riga con le statistiche di ogni esecuzione mentre V$SQLAREA riporta le statistiche in modo aggregato per ogni statement SQL.
La vista V$SQLSTATS e' stata introdotta nella 10g R2, e' piu' efficiente della V$SQLAREA ed ha una ritenzione maggiore.

Le tre viste hanno molte colonne colonne in comune (eg. SQL_ID, EXECUTIONS, CPU_TIME, BUFFER_GETS, PLAN_HASH_VALUE, ...) ma vi sono alcune piccole ma importanti differenze.
Uno statement SQL puo' avere uno o piu' child cursor [NdA detti anche version]. La vista V$SQL ha una riga per ogni child cursor mentre la vista V$SQLAREA riporta i totali. Quando V$SQLAREA ha VERSION_COUNT>1 vi sono piu' child cursor in V$SQL con CHILD_NUMBER differenti. Analizzando la V$SQL_SHARED_CURSOR e' possibile determinare perche' vengono creati child differenti.
Anche la vista V$SQLSTATS riporta dati totali ma e' implementata in modo differente richiedendo un numero di lock inferiore: in pratica risulta tipicamente molto piu' veloce. Nella vista V$SQLSTATS e' disponibile l'intero statement SQL ma sono disponibili nettamente meno colonne (eg. PARSING_SCHEMA_NAME).
Con Oracle RAC tipicamente si utilizzano le viste GV$ che anologhe alle viste V$ ma riportano dati globali per l'intero cluster.

Altre viste utili sono V$SESSION, V$SQL_PLAN, V$SQLTEXT, ...

Varie ed eventuali...

Pur mantenendo la stessa logica sono significative le differenze tra le versioni di Oracle.


Titolo: SQL tuning in Oracle 11gR2
Livello: Avanzato (3/5)
Data: 1 Aprile 2014
Versione: 1.0.2 - 1 Aprile 2017
Autore: mail [AT] meo.bogliolo.name