In 2015 heb ik onze Oracle 11.2.0.4-databases geüpgraded naar 12.1.0.2 en er waren enkele prestatieproblemen met betrekking tot ons gebruik van GTT's. Over die problemen heb ik hier geblogd.
De kern van het probleem dat ik probeerde op te lossen, was dat een gedragsverandering in 12c ertoe leidde dat Oracle statistieken opsloeg dat de GTT nul rijen heeft als dat niet het geval is. Statistieken die aangeven dat het aantal rijen gelijk is aan nul, leiden tot volledige tabelscans en cartesiaanse producten bij zoekopdrachten waarbij de GTT betrokken is. Zoals ik in die blogpost al zei, gebruikten we DBMS_STATS.SET_TABLE_STATS nadat we de tabel met gegevens hadden gevuld, zodat elke sessie de juiste statistieken zou hebben om tot een beter uitvoeringsplan te komen.
Nadat we een upgrade naar Oracle 19c hadden uitgevoerd, begonnen we andere prestatieproblemen met betrekking tot de GTT te zien. Query's die de GTT gebruikten, begonnen te wachten op de "cursor pin:S wait on X" wait-gebeurtenis. Dit kan een gedragsverandering zijn geweest met de nieuwe Oracle-versie, maar het kan ook zijn dat onze ontwikkelaars de GTT vaker in onze code hebben gebruikt en niets te maken hebben met de nieuwe versie.
Voor de query's die betrokken zijn bij de Cursor Pin wait-gebeurtenis, zag ik een groot aantal versies van de SQL-instructie in de Shared Pool. Toen ik V$SQL_SHARED_CURSOR opvroeg, ontdekte ik dat PURGED_CURSOR='Y' voor deze SQL-statements. De cursor wordt ongeldig.
Toen ik dit probleem onderzocht, ontdekte ik dat wat er gebeurt, is dat elke keer dat we DBMS_STATS.SET_TABLE_STATS aanroepen om sessiegebaseerde statistieken over de GTT te krijgen, het alle SQL-instructies ongeldig maakt die die GTT gebruiken. Vandaar het wachten. Het wachten was niet lang, dus veel eindgebruikers merkten het probleem niet eens op.
Maar toen hadden we een nieuw probleem. Wanneer u SET_TABLE_STATS aanroept, schrijft Oracle een item in SYS.WRI$_OPTSTAT_TAB_HISTORY en kunt u de waarden zien die de sessie heeft ingesteld voor de tabelstatistieken. Standaard slaat deze tabel 30 dagen geschiedenis op. De tafel groeide zeer sterk en verbruikte het grootste deel van SYSAUX. Om de zoveel tijd (per uur?) verwijdert Oracle vermeldingen die meer dan 30 dagen oud zijn. Dit regelmatig snoeien van deze tabel had nu een negatieve invloed op de prestaties van de eindgebruiker. Het volgende is een prestatiegrafiek van Lighty die de impact van het snoeien van deze tabel laat zien:
Al die enge rode kleur was toen de oude rijen werden verwijderd uit SYS.WRI$_OPTSTAT_TAB_HISTORY.
Dus mijn prestatie "fix" vijf jaar geleden introduceerde een ander prestatieprobleem. Om de prestaties te verbeteren, maakte ik gedeelde statistieken op de GTT en stopte ik met het gebruik van sessiestatistieken. Dit zijn de stappen:
--set prefs to SHARED globally
exec DBMS_STATS.set_global_prefs ( pname => 'GLOBAL_TEMP_TABLE_STATS', pvalue => 'SHARED');
--set the table and index stats
exec dbms_stats.set_table_stats(ownname=>'MY_SCHEMA',tabname=>'MY_GTT_TABLE',numrows=>1000,numblks=>2,avgrlen=>15);
exec dbms_stats.set_index_stats(ownname=>'MY_SCHEMA',indname=>'GTT_INDEX',indlevel=>1,numlblks=>2,numdist=>15,clstfct=>28,numrows=>1000);
-- set prefs back to SESSION
exec DBMS_STATS.set_global_prefs ( pname => 'GLOBAL_TEMP_TABLE_STATS', pvalue => 'SESSION');
-- verify stats set
select num_rows,blocks,last_analyzed,scope
from dba_tab_statistics
where table_name ='MY_GTT_TABLE';
select blevel,leaf_blocks,distinct_keys,num_rows,clustering_factor,last_analyzed,scope
from dba_ind_statistics
where index_name='GTT_INDEX' and owner='MY_SCHEMA';
Zodra de gedeelde statistieken aanwezig waren, verwijderen we de aanroepen naar DBMS_SET_TABLE_STATS uit onze code.