Oracle не використовує унікальний індекс для довгого ключа


16

У мене в тестовій базі даних є таблиця з 250K рядками. (У виробництві є кілька сотень мільйонів, ми можемо спостерігати ту саму проблему.) У таблиці є рядковий ідентифікатор nvarchar2 (50), не нульовий, з унікальним індексом на ньому (це не ПК).

Ідентифікатори складаються з першої частини, яка містить 8 різних значень у моїй тестовій базі даних (і близько тисячі у виробництві), потім знак @ і, нарешті, число, довжиною від 1 до 6 цифр. Наприклад, може бути 50 тисяч рядків, які починаються з "ABCD_BGX1741F_2006_13_20110808.xml @", а за ним слідує 50 тисяч різних цифр.

Коли я запитую один рядок на основі його ідентифікатора, кардинальність оцінюється як 1, вартість дуже низька, він працює чудово. Коли я запитую більше ніж один рядок з декількома ідентифікаторами у виразі IN чи ІЛИ, то оцінки індексу є абсолютно неправильними, тому використовується повне сканування таблиці. Якщо я змушую індекс натяком, це дуже швидко, сканування повної таблиці фактично виконується на порядок повільніше (і набагато повільніше у виробництві). Тож це проблема оптимізатора.

Як тест, я дублював таблицю (в тій же схемі + простір таблиць) з точно таким же DDL і точно таким же вмістом. Я відтворив унікальний індекс на першій таблиці для гарної міри та створив такий самий індекс на таблиці клонування. Я зробив а DBMS_STATS.GATHER_SCHEMA_STATS('schemaname',estimate_percent=>100,cascade=>true);. Ви навіть можете побачити, що імена індексів є послідовними. Тож тепер єдина відмінність між двома таблицями полягає в тому, що перша була завантажена у випадковому порядку протягом тривалого періоду, з блоками, розкиданими на диску (у табличному просторі разом з кількома іншими великими таблицями), друга завантажувалася як одна партія ВСТАВИТЬ-ВИБІР. Крім цього, я не можу уявити ніякої різниці. (Початкова таблиця була зменшена з моменту останнього великого видалення; після цього не було жодного видалення.)

Ось плани запитів для хворих та клоніруючої таблиці (Рядки під чорною щіткою однакові по всьому малюнку, а також під сірою кистю.):

плани запитів

(У цьому прикладі є 1867 рядків, які починаються з чорного пензликом ідентифікатора. 2-рядовий запит створює кардинальність 1867 * 2, 3-рядовий запит створює кардинальність 1867 * 3 тощо. Не можна Будь збіг обставин, схоже, Oracle не піклується про кінець ідентифікаторів.)

Що може спричинити таку поведінку? Очевидно, було б досить дорого відтворити стіл у виробництві.

USER_TABLES: http://i.stack.imgur.com/nDWze.jpg USER_INDEXES: http://i.stack.imgur.com/DG9um.jpg Я змінив лише ім'я схеми та простору таблиць. Ви можете бачити, що назви таблиці та індексу такі ж, як на скріншоті плану запитів.

Відповіді:


7

(Це відповідає на інше питання про те, чому гістограми різні.)

Гістограми створюються за замовчуванням на основі перекосу стовпця та того, чи використовується стовпець у відповідному предикаті. Копіювання DDL та даних недостатньо, важлива також інформація про навантаження.

Відповідно до Посібника з настройки продуктивності :

Коли ви опускаєте таблицю, інформація про робоче навантаження, яка використовується функцією збору автогістограми та збережена історія статистики, що використовується процедурами RESTORE _ * _ STATS, втрачається. Без цих даних ці функції не працюють належним чином.

Наприклад, ось таблиця зі скошеними даними, але не має гістограми:

drop table test1;
create table test1(a date);
insert into test1 select date '2000-01-01'+level from dual connect by level <= 10;
insert into test1 select date '2000-01-01' from dual connect by level <= 1000;
begin
    dbms_stats.gather_table_stats(user, 'TEST1');
end;
/
select histogram from user_tab_columns where table_name = 'TEST1';

HISTOGRAM
---------
NONE

Запустивши те саме, але із запитом до того, як зібрати статистику, генеруватиме гістограму.

drop table test1;
create table test1(a date);
insert into test1 select date '2000-01-01'+level from dual connect by level <= 10;
insert into test1 select date '2000-01-01' from dual connect by level <= 1000;
select count(*) from test1 where a = sysdate; --Only new line
begin
    dbms_stats.gather_table_stats(user, 'TEST1');
end;
/
select histogram from user_tab_columns where table_name = 'TEST1';

HISTOGRAM
---------
FREQUENCY

2
Блискуче простий приклад. Чи маєте ви уявлення, чому CBO використовував гістограми для оцінки кардинальності при унікальному скануванні, а не просто припускаючи 1?
Джек каже, спробуйте topanswers.xyz

Спасибі! Я зробив повне запитання щодо своїх даних та запитів у своєму блозі: joco.name/2014/01/05/…
fejesjoco

@Jack Я думаю, що це лінь. Інженери Oracle, мабуть, подумали, що статистика унікального індексу матиме таку ж кількість відмінних значень, що й рядки, тому припущення про кардинальність не є провідним, а просто використовується зі статистики, як у будь-якому іншому випадку. Також, як загальний випадок, гістограми козирують простою статистикою. Моя справа здається дуже особливою лише через довгі клавіші, але я вважаю, що це працює дуже добре інакше.
fejesjoco

@fejesjoco Я думаю , що пояснення JL є більш вірогідним, оскільки гістограми також мали б загальну статистику у випадку одного пошуку (без цього in), чи не так? Я думаю, що CBO робить припущення про кардинальність 1, але лише у дуже простому випадку. Я припускаю, що ви могли б UNION ALLвирішити все, використовуючи велику, але можуть бути й інші причини цього не робити, і JL згадує інші можливі шляхи вирішення у пов'язаній публікації блогу.
Джек каже, спробуйте topanswers.xyz

1
Ще одна незначна таємниця, яку слід врахувати - як в першу чергу була створена ця гістограма? Oracle, здається, вважає стовпчик перекошеним лише у тому випадку, якщо він має дублікати, що, очевидно, не може мати ваш унікальний стовпець. Хтось навмисно будував цю гістограму (навряд чи), чи хтось збирав статистику з нерекомендованими method_opt=>'for all indexed columns'?
Джон Хеллер

8

Я знайшов рішення! Це так красиво, і я насправді дізнався багато про Oracle.

Одним словом: гістограми.

Я почав багато читати про те, як працює CBO Oracle, і я натрапив на гістограми. Я не повністю зрозумів, тому я подивився на таблицю USER_HISTOGRAMS та voilá. Для хворого столу було кілька рядів, а для клонованого столу практично нічого. Для таблиці хворих був один ряд для кожної з 8 різних ідентифікатор-початкових частин. І це головне: вони були відрізані на 32 символи, перед знаком @. Як я вже сказав, перша частина клавіш сильно повторюється, вони стають різними після знаку @.

Здається, що гістограми можуть бути більш потужними, ніж простий факт, що унікальний індекс завжди має кардинальність 0 або 1 для заданого значення. Коли я запитував 2+ рядків, Oracle дивився на гістограму, думав, що може бути десятки тисяч значень для цієї ідентифікатор-початкової частини, і це скинуло CBO з курсу.

Я видалив гістограми для цього стовпця зі старої таблиці, і проблема пішла!

Більше читання: https://blogs.oracle.com/optimizer/entry/how_do_i_drop_an_existing_histogram_on_a_column_and_stop_the_auto_stats_gathering_job_from_creating


2
Я згадав, що в нашій кімнаті чатів :) chat.stackexchange.com/transcript/message/12987649#12987649
Philᵀᴹ

Я цього не бачив :). Тож єдине дивне, чому в першій таблиці були гістограми, а не в клоні, я думав, що збирати_schema_stats оновило все, мабуть, ні.
fejesjoco

6

Я надіслав електронною поштою Джонатану Льюїсу про це і отримав дуже корисну відповідь:

Незвичайність в обчисленні є наслідком обмежень на символьних гістограмах, зокрема див.

http://jonathanlewis.wordpress.com/2010/10/13/frequency-histogram-5/ http://jonathanlewis.wordpress.com/2010/10/19/frequency-histograms-6/

Дивлячись на приклад, запит призначений для списку IN, а не для одного рядка, тому моїм початковим припущенням буде те, що оптимізатор використовував загальну стратегію для обчислення селективності декількох рядків, а не спеціальний фрагмент коду для Список IN в первинному ключі. Я думаю, їм було б нелегко розпізнати цю справу, але, напевно, розробники не вважали, що варто докладати зусиль.

Я настійно рекомендую читати повідомлення в блогах, на які він посилається, вони детально описують обмеження гістограм, до яких ви працюєте, наприклад:

Висновок : Якщо у вас є досить довгі і подібні рядки в стовпці, який є хорошим кандидатом для частотної гістограми (наприклад, дуже описовий стовпець статусу), у вас виникає проблема, якщо значення, яке дуже рідкісне, виглядає ідентичним дуже популярному значення до перших 32 символів. Ви можете виявити, що єдиним рішенням є зміна списку юридичних значень (хоча різні стратегії, що включають віртуальні стовпці або на основі функцій, можуть усунути проблему).


На жаль, гістограми здаються маловідомою особливістю, я думаю, це тому, що це занадто глибоко для розробника SQL і більшу частину часу вони просто працюють, але добре знати, що про це багато ресурсів, я просто не шукав потрібні місця :). Дуже погано, що Oracle скорочує 32 байти і приймає катастрофічні рішення на основі цього. На щастя, мені не потрібно жодних налаштувань, скидання гістограм є ідеальним рішенням. Ключові значення унікальні, я завжди шукаю 20 значень одночасно, він добре працює лише з індексом, і це детерміновано. Але я не використовуватиму довгі клавіші наступного разу, це точно.
fejesjoco

Гістограми досить добре відомі серед DBA;) Мені подобається те, що ти, схоже, прагнеш вивчити глибші речі і справді думаєш, що ти повинен прочитати книгу JL, це дуже дуже добре. Як правило, ЦБ робить чудову роботу: завжди будуть кращі справи, які потребують розслідування, але варто враховувати, що навіть без відсікання, оцінки завжди є лише кошторисом.
Джек каже, спробуйте topanswers.xyz

1
Якщо ви виконуєте звичайну роботу зі статистикою (як, наприклад, Oracle працює за замовчуванням при чистому встановленні), ви можете виявити, що гістограми з’являються знову, можливо, вам доведеться розглянути спосіб запобігання цьому (наприклад, LOCK_TABLE_STATS ),
каже Джек, спробуйте topanswers. xyz

У своїй відповіді я згадав допис у блозі, є вказівки щодо запобігання гістограми для стовпчика.
fejesjoco

1
@Jack Douglas, дякую за залучення Дж. Льюїса та звітування назад!
Димитре Радулов
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.