тормоза при fetch: магическая цифра 56

Запросы, планы, оптимизация запросов, ...

Модераторы: kdv, CyberMax

Ответить
vkruglik
Сообщения: 5
Зарегистрирован: 22 авг 2006, 19:57

тормоза при fetch: магическая цифра 56

Сообщение vkruglik » 22 авг 2006, 20:18

Проблема следующая. Есть запрос:

Код: Выделить всё

select * from master_table
where (master_table.id not in (select parent_id from linked_table where upper(field1)='СТРОКА1' and (upper(field2)='СТРОКА2' or upper(field2)='СТРОКА3' or upper(field2)='СТРОКА4')))
Таблица master_table содержит около 150 тыс. записей, таблица linked_table - примерно столько же, в результате запроса в in (...) получается около 2000 записей.

Проблема в следующем: fetch из результата запроса происходит порциями по 56 записей (как в собственном приложении, так и в IBExpert-е). То есть фетчится 56 записей, потом FB отъедает 100% CPU на несколько секунд, затем выдает очередные 56 записей. Подскажите, почему?

Сервер FB 2.0 RC4. Ранее все работало на RC1 и проблемы не было.

Adapted Plan
PLAN (LINKED_TABLE INDEX (IDX_LINKED_TABLE_FIELD2, IDX_LINKED_TABLE_FIELD2, IDX_LINKED_TABLE_FIELD2, IDX_LINKED_TABLE_FIELD1)) PLAN (MASTER_TABLE NATURAL)

------ Performance info ------
Prepare time = 10ms
Execute time = 721ms
Avg fetch time = 90,13 ms
Current memory = 767 688
Max memory = 795 840
Memory buffers = 2 048
Reads from disk to cache = 102
Writes from cache to disk = 6
Fetches from cache = 416 267

На RC1 план был такой:
Adapted Plan
PLAN (LINKED_TABLE INDEX (FK_LINKED_TABLE_PARENT_ID)) PLAN (MASTER_TABLE NATURAL)

------ Performance info ------
Prepare time = 0ms
Execute time = 371ms
Avg fetch time = 46,38 ms
Current memory = 747 092
Max memory = 775 220
Memory buffers = 2 048
Reads from disk to cache = 30
Writes from cache to disk = 6
Fetches from cache = 1 173

Подскажите, в чем может быть проблема? Переписать запрос без использования WHERE к сожалению не получится - клиентское приложение требует такой конструкции.

Насколько я понимаю, RC4 стал строить другой план? Как его заставить вернуться к прежнему плану?

Dimitry Sibiryakov
Заслуженный разработчик
Сообщения: 1436
Зарегистрирован: 15 сен 2005, 09:05

Сообщение Dimitry Sibiryakov » 23 авг 2006, 08:18

Вся проблема в not in. Он работал неправильно (давал неправильные результаты) и поэтому ему запретили использовать индексы. Переписывай на JOIN.

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 23 авг 2006, 11:22

Код: Выделить всё

select m.*
  from master_table m left join linked_table l on (l.parent_id = m.id)
  where l.parent_id is null or
        (upper(l.field1)!='СТРОКА1' or
         upper(l.field2)!='СТРОКА2' and upper(l.field2)!='СТРОКА3' and upper(l.field2)!='СТРОКА4')
или

Код: Выделить всё

select m.*
  from master_table m
  where not exists (select *
                      from linked_table
                      where parent_id=m.id and upper(field1)='СТРОКА1' and
                            (upper(field2)='СТРОКА2' or upper(field2)='СТРОКА3' or upper(field2)='СТРОКА4'))

vkruglik
Сообщения: 5
Зарегистрирован: 22 авг 2006, 19:57

Сообщение vkruglik » 23 авг 2006, 11:32

Dimitry Sibiryakov писал(а):Вся проблема в not in. Он работал неправильно (давал неправильные результаты) и поэтому ему запретили использовать индексы. Переписывай на JOIN.
Опс. А запретили навсегда? Или в окончательном релизе 2.0 исправят? Я пока откатился на RC1, ибо у меня глюков с not in не замечено.

А если переписать на not exists (...)? Надо будет попробовать, о результате доложу.

dimitr
Разработчик Firebird
Сообщения: 888
Зарегистрирован: 26 окт 2004, 16:20

Сообщение dimitr » 25 авг 2006, 10:13

запретили в ветке 2.x. В будущих версиях можно надеяться на лучшее.

про NOT EXISTS - он не эквивалентен NOT IN в случае наличия нуллов.

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 25 авг 2006, 10:48

dimitr писал(а):про NOT EXISTS - он не эквивалентен NOT IN в случае наличия нуллов.
А ссылочку на пример? Что-то не встречалось, или просмотрел, интересно :)

dimitr
Разработчик Firebird
Сообщения: 888
Зарегистрирован: 26 окт 2004, 16:20

Сообщение dimitr » 25 авг 2006, 11:07

Код: Выделить всё

recreate table tab1 (col int);

insert into tab1 values (null);
insert into tab1 values (1);
insert into tab1 values (2);

select count(*) from rdb$database
where 0 not in ( select col from tab1 )

select count(*) from rdb$database
where not exists ( select * from tab1 where col = 0 )
сравни результаты

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 25 авг 2006, 12:07

А. Я и сам мог бы догадаться, что к вышеописанной ситуации отношения никакого...
К тому же твой пример не совсем показателен, ведь можно:

Код: Выделить всё

select count(*) from rdb$database 
where not exists ( select * from tab1 where col = 0 or col is null )
У тебя null получается не в not exists, а в where, что немного не то.

dimitr
Разработчик Firebird
Сообщения: 888
Зарегистрирован: 26 окт 2004, 16:20

Сообщение dimitr » 25 авг 2006, 13:02

тупая замена IN на EXISTS - это перенос предиката в WHERE подзапроса. Что я и продемонстрировал, вкупе с [довольно неочевидным] побочным эффектом.

vkruglik
Сообщения: 5
Зарегистрирован: 22 авг 2006, 19:57

Сообщение vkruglik » 27 окт 2006, 21:56

в общем руки дошли только теперь, после перехода на FB2RC5, в котором указанная проблема также не решилась. ;-(

пришлось переписать на not exists. работает.

Ответить