W 2015 roku zaktualizowałem nasze bazy danych Oracle 11.2.0.4 do 12.1.0.2 i napotkałem pewne problemy z wydajnością związane z korzystaniem z GTT. Pisałem o tych problemach tutaj.
Sednem problemu, który próbowałem rozwiązać, było to, że zmiana zachowania w 12c prowadzi do oszczędności statystyk Oracle, że GTT ma zero wierszy, gdy nie ma. Statystyki pokazujące liczbę wierszy równą zeru prowadzi do skanów pełnych tabel i produktów kartezjańskich w zapytaniach, które obejmują GTT. Jak wspomniałem w tym poście na blogu, użyliśmy DBMS_STATS.SET_TABLE_STATS po wypełnieniu tabeli danymi, aby każda sesja miała odpowiednie statystyki, aby uzyskać lepszy plan wykonania.
Po przejściu na Oracle 19c zaczęliśmy dostrzegać inne problemy z wydajnością związane z GTT. Zapytania, które używały GTT, zaczęły czekać na zdarzenie oczekiwania „kursor pin:S czekaj na X”. Mogła to być zmiana zachowania w przypadku nowej wersji Oracle, ale mogło to być również powodem, dla którego nasi programiści częściej używali GTT w naszym kodzie i nie mieli nic wspólnego z nową wersją.
W przypadku zapytań związanych ze zdarzeniem oczekiwania na przypięcie kursora zauważyłem dużą liczbę wersji instrukcji SQL w puli współdzielonej. Kiedy zapytałem V $ SQL_SHARED_CURSOR, odkryłem, że PURGED_CURSOR =„Y” dla tych instrukcji SQL. Kursor zostaje unieważniony.
Badając ten problem, odkryłem, że za każdym razem, gdy wywołaliśmy DBMS_STATS.SET_TABLE_STATS, aby uzyskać statystyki sesji GTT, unieważnia to wszystkie instrukcje SQL, które używają tego GTT. Stąd czekanie. Oczekiwanie nie trwało długo, więc wielu użytkowników końcowych nawet nie zauważyło problemu.
Ale wtedy pojawił się nowy problem. Kiedy wywołujesz SET_TABLE_STATS, Oracle zapisuje wpis w SYS.WRI$_OPTSTAT_TAB_HISTORY i możesz zobaczyć wartości ustawione przez sesję dla statystyk tabeli. Domyślnie ta tabela przechowuje 30 dni historii. Stół bardzo się rozrósł i pochłaniał większość SYSAUX. Co jakiś czas (co godzinę?) Oracle usuwa wpisy starsze niż 30 dni. To regularne przycinanie tej tabeli miało teraz negatywny wpływ na wydajność użytkownika końcowego. Poniżej znajduje się wykres wydajności firmy Lighty pokazujący wpływ przycinania tej tabeli:
Cały ten przerażający czerwony kolor jest wtedy, gdy stare wiersze były usuwane z SYS.WRI$_OPTSTAT_TAB_HISTORY.
Tak więc moja „poprawka” wydajności pięć lat temu wprowadziła kolejny problem z wydajnością. Aby poprawić wydajność, stworzyłem wspólne statystyki w GTT i przestałem używać statystyk sesji. Oto kroki:
--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';
Po wprowadzeniu wspólnych statystyk usuwamy z naszego kodu wywołania DBMS_SET_TABLE_STATS.