Im Jahr 2015 habe ich unsere Oracle 11.2.0.4-Datenbanken auf 12.1.0.2 aktualisiert und dabei einige Leistungsprobleme im Zusammenhang mit unserer Verwendung von GTTs festgestellt. Über diese Probleme habe ich hier gebloggt.
Der Kern des Problems, das ich zu lösen versuchte, war, dass eine Verhaltensänderung in 12c dazu führte, dass Oracle Statistiken speicherte, dass die GTT null Zeilen hat, wenn dies nicht der Fall ist. Statistiken, die zeigen, dass die Anzahl der Zeilen gleich Null ist, führen zu vollständigen Tabellenscans und kartesischen Produkten bei Abfragen, die die GTT beinhalten. Wie ich in diesem Blogbeitrag erwähnt habe, haben wir DBMS_STATS.SET_TABLE_STATS verwendet, nachdem wir die Tabelle mit Daten gefüllt hatten, damit jede Sitzung über die richtigen Statistiken verfügt, um zu einem besseren Ausführungsplan zu gelangen.
Nach dem Upgrade auf Oracle 19c begannen wir, andere Leistungsprobleme im Zusammenhang mit GTT zu sehen. Abfragen, die die GTT verwendeten, begannen mit dem Warten auf das Warteereignis „Cursor-Pin:S warten auf X“. Dies könnte eine Verhaltensänderung mit der neuen Oracle-Version gewesen sein, aber es könnte auch daran gelegen haben, dass unsere Entwickler die GTT häufiger in unserem Code verwenden und nichts mit der neuen Version zu tun haben.
Bei den Abfragen des Wait-Ereignisses „Cursor Pin“ ist mir eine hohe Anzahl von Versionen der SQL-Anweisung im Shared Pool aufgefallen. Als ich V$SQL_SHARED_CURSOR abfragte, entdeckte ich, dass PURGED_CURSOR=’Y’ für diese SQL-Anweisungen. Der Cursor wird ungültig.
Bei der Untersuchung dieses Problems stellte ich fest, dass jedes Mal, wenn wir DBMS_STATS.SET_TABLE_STATS aufriefen, um sitzungsbasierte Statistiken zur GTT zu erhalten, alle SQL-Anweisungen, die diese GTT verwenden, ungültig werden. Daher die Wartezeit. Die Wartezeit war nicht lang, so dass viele Endbenutzer das Problem nicht einmal bemerkten.
Aber dann hatten wir ein neues Problem. Wenn Sie SET_TABLE_STATS aufrufen, schreibt Oracle einen Eintrag in SYS.WRI$_OPTSTAT_TAB_HISTORY und Sie können die Werte sehen, die die Sitzung für die Statistiken der Tabelle festgelegt hat. Standardmäßig speichert diese Tabelle einen Verlauf von 30 Tagen. Die Tabelle wuchs sehr stark und verbrauchte den größten Teil von SYSAUX. Gelegentlich (stündlich?) entfernt Oracle Einträge, die älter als 30 Tage sind. Diese regelmäßige Bereinigung dieser Tabelle wirkte sich nun negativ auf die Endbenutzerleistung aus. Das Folgende ist ein Leistungsdiagramm von Lighty, das die Auswirkungen der Beschneidung dieser Tabelle zeigt:
All diese beängstigende rote Farbe ist, als die alten Zeilen aus SYS.WRI$_OPTSTAT_TAB_HISTORY entfernt wurden.
Mein Performance-„Fix“ vor fünf Jahren führte also zu einem weiteren Performance-Problem. Um die Leistung zu verbessern, habe ich gemeinsame Statistiken auf dem GTT erstellt und die Verwendung von Sitzungsstatistiken eingestellt. Hier sind die Schritte:
--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';
Sobald die gemeinsam genutzten Statistiken vorhanden waren, entfernen wir die Aufrufe von DBMS_SET_TABLE_STATS aus unserem Code.