Още през 2015 г. надстроих нашите бази данни Oracle 11.2.0.4 до 12.1.0.2 и изпитах някои проблеми с производителността, свързани с използването на GTT. Водих блог за тези проблеми тук.
Същността на проблема, който се опитвах да разреша, беше, че промяната на поведението в 12c води до запазване на статистически данни на Oracle, че GTT има нулеви редове, когато не го прави. Статистическите данни, показващи броя на редовете, равен на нула, водят до пълно сканиране на таблицата и декартови продукти за заявки, които включват GTT. Както казах в тази публикация в блога, използвахме DBMS_STATS.SET_TABLE_STATS, след като попълнихме таблицата с данни, така че всяка сесия да има правилни статистически данни, за да стигнем до по-добър план за изпълнение.
След като надстроихме до Oracle 19c, започнахме да виждаме други проблеми с производителността, свързани с GTT. Заявките, които са използвали GTT, започнаха да чакат на събитието за изчакване „cursor pin:S wait on X“. Това можеше да е промяна в поведението с новата версия на Oracle, но също така можеше нашите разработчици да използват GTT по-често в нашия код и да нямат нищо общо с новата версия.
За заявките, включени в събитието Cursor Pin wait, забелязах голям брой версии на SQL израза в споделения пул. Когато попитах V$SQL_SHARED_CURSOR, открих, че PURGED_CURSOR=’Y’ за тези SQL изрази. Курсорът става невалиден.
Когато проучвах този проблем, открих, че това, което се случва, е, че всеки път, когато извикаме DBMS_STATS.SET_TABLE_STATS, за да получим базирани на сесия статистически данни за GTT, той обезсилва всички SQL оператори, които използват този GTT. Оттук и чакането. Чакането не беше дълго, така че много крайни потребители дори не забелязаха проблема.
Но тогава имахме нов проблем. Когато направите повикване към SET_TABLE_STATS, Oracle записва запис в SYS.WRI$_OPTSTAT_TAB_HISTORY и можете да видите стойностите, зададени от сесията за статистиката на таблицата. По подразбиране тази таблица съхранява 30 дни история. Таблицата растеше много и поглъщаше по-голямата част от SYSAUX. От време на време (на час?) Oracle ще премахва записи на повече от 30 дни. Това редовно съкращаване на тази таблица вече се отразява негативно на производителността на крайния потребител. Следва графика на производителността от Lighty, показваща въздействието на подрязването на тази таблица:
Целият този страшен червен цвят е когато старите редове бяха премахнати от SYS.WRI$_OPTSTAT_TAB_HISTORY.
Така че моето „поправяне“ на представянето преди пет години въведе друг проблем с производителността. За да подобря производителността, това, което направих, беше да създам споделени статистически данни на GTT и да спра да използвам статистически данни за сесиите. Ето стъпките:
--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';
След като споделените статистически данни са на място, премахваме извикванията към DBMS_SET_TABLE_STATS от нашия код.