Sql serve Полнотекстовый поиск с Containstable очень медленный при использовании в JOIN! - PullRequest
13 голосов
/ 01 мая 2010

Я использую полнотекстовый поиск sql 2008, и у меня возникают серьезные проблемы с производительностью в зависимости от того, как я использую Contains или ContainsTable.

Вот пример: (в первой таблице содержится около 5000 записей, а в таблице table1 есть покрытый индекс, в котором есть все поля в предложении where. Я попытался упростить операторы, так что извините, если есть синтаксические проблемы.)

Сценарий 1:

select * from table1 as t1
where t1.field1=90
and   t1.field2='something'
and   Exists(select top 1 * from containstable(table1,*, 'something') as t2 
where t2.[key]=t1.id)

Результаты: 10 секунд (очень медленно)

Сценарий 2:

select * from table1 as t1
join containstable(table1,*, 'something') as t2 on t2.[key] = t1.id
where t1.field1=90
and   t1.field2='something'

результаты: 10 секунд (очень медленно)

Сценарий 3:

Declare @tbl Table(id uniqueidentifier primary key)
insert into @tbl select {key] from containstable(table1,*, 'something')

select * from table1 as t1
where t1.field1=90
and   t1.field2='something'
and  Exists(select id from @tbl as tbl where id=req1.id)

результаты: доля секунды (супер быстрая)

Суть в том, что, если я использую Containstable в любом виде соединения или в условии условия предложения оператора select, который также имеет другие условия, производительность действительно плохая. Кроме того, если вы посмотрите на профилировщик, количество операций чтения из базы данных возрастет. Но если я сначала сделаю полнотекстовый поиск и поместу результаты в табличную переменную и использую эту переменную, все пойдет очень быстро. Количество операций чтения также значительно ниже. Это кажется в «плохих» сценариях, так или иначе, это застревает в цикле, который заставляет его многократно читать из базы данных, но, конечно, я не понимаю почему.

Теперь вопрос, прежде всего, почему это происходит? и вопрос два в том, насколько масштабируемые переменные таблицы? Что, если это приведет к десяткам тысяч записей? это все еще будет быстро.

Есть идеи? Спасибо

Ответы [ 3 ]

12 голосов
/ 04 мая 2010

Я потратил довольно много времени на эту проблему, и, исходя из множества сценариев, я понял это:

если у вас есть Contains или ContainsTable где-либо в вашем запросе, то эта часть выполняется в первую очередь и довольно независимо. Это означает, что даже если остальные условия ограничивают ваш поиск только одной записью, это ни содержит, ни содержит в себе заботы об этом. Так что это похоже на параллельное выполнение.

Теперь, когда полнотекстовый поиск возвращает только поле Key, он немедленно ищет Key как первое поле других индексов, выбранных для запроса. Таким образом, для приведенного выше примера он ищет индекс с помощью [key], field1, field2. Проблема в том, что он выбирает индекс для остальной части запроса на основе полей в предложении where. так что для примера выше он выбирает покрытый индекс, который у меня есть, что-то вроде field1, field2, Id. (Идентификатор таблицы такой же, как [Ключ], возвращаемый из полнотекстового поиска). Итак, резюме:

  1. выполняет содержащийся в таблице
  2. выполняет оставшуюся часть запроса и выбирает индекс на основе предложения where запроса
  3. Он пытается объединить эти два. Следовательно, если индекс, выбранный для остальной части запроса, начинается с поля [key], это нормально. Однако, если индекс не имеет поля [key] в качестве первого ключа, он начинает выполнять циклы. Он даже не выполняет сканирование таблицы, иначе просмотр 5000 записей не будет таким медленным. То, как он выполняет цикл, заключается в том, что он запускает цикл для общего числа результатов от FTS, умноженного на общее количество результатов из остальной части запроса. Таким образом, если FTS возвращает 2000 записей, а остальная часть запроса возвращает 3000, цикл 2000 * 3000 = 6 000 000. Я не понимаю, почему.

Так что в моем случае он выполняет полнотекстовый поиск, затем выполняет остальную часть запроса, но выбирает закрытый индекс, который у меня есть, который основан на field1, field2, id (что неверно), и в результате он привинчивает вверх. Если бы я изменил свой покрытый индекс на Id, field1, field2, все было бы очень быстро.

Мое ожидание состояло в том, что FTS возвращает группу [key], остальная часть запроса возвращает группу [Id], а затем Id должен быть сопоставлен с [key].

Конечно, я попытался упростить свой запрос здесь, но фактический запрос намного сложнее, и я не могу просто изменить индекс. У меня также есть сценарии, в которых текст, переданный в виде полного текста, является пустым, и в этих сценариях я даже не хочу объединяться с содержащимися таблицами. В этих случаях изменение моего покрытого индекса на поле id в качестве первого поля приведет к катастрофе.

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

спасибо

7 голосов
/ 04 мая 2010

Обычно это работает очень быстро:

select t1.*, t2.Rank
    from containstable(table1, field2, 'something') as t2
        join table1 as t1 ON t1.id = t2.Key AND t1.field1=90
    order by t2.Rank desc

Существует большая разница в том, где вы указали критерии поиска: в JOIN или в WHERE.

4 голосов
/ 03 мая 2010

Я собираюсь предположить, что ваша проблема такая же, как и в другой теме, с которой я связан. Вы находите проблему, возникающую при поиске по нескольким словам?

Если так, то мой ответ из этой темы будет применяться.

С http://technet.microsoft.com/en-us/library/cc721269.aspx#_Toc202506240

Самое главное, что правильный тип соединения выбран для полнотекстовый запрос. мощность оценка на FulltextMatch STVF очень важно для правильного плана. Итак, первое, что нужно проверить, это FulltextMatch оценка мощности. Это приблизительное количество хитов в указателе для полнотекстового поиска строка. Например, в запросе в Рисунок 3 это должно быть близко к количество документов, содержащих термин «слово». В большинстве случаев это должно быть очень точным, но если оценка был далеко, вы могли генерировать плохие планы. Оценка для отдельные термины, как правило, очень хорошо, но оценивая несколько терминов, таких как фразы или AND-запросы более сложны поскольку невозможно узнать что пересечение терминов в индексе будет основываться на частоте условия в индексе. Если мощность оценка хорошая, плохой план вероятно вызвано запросом оптимизатор стоимости модели. Единственный способ исправить проблему плана - использовать запрос намек на принудительное соединение или оптимизировать для.

Таким образом, по информации, хранящейся в нем, он просто не может знать, могут ли два поисковых термина быть вместе достаточно независимыми или обычно встречаться вместе. Возможно, вам нужно иметь две отдельные процедуры: одну для запросов с одним словом, для которой вы позволяете оптимизатору выполнять свою работу, и одну для условий поиска из нескольких слов, для которых вы навязываете «достаточно хороший» план (sys.dm_fts_index_keywords может помочь, если вы хотите выполнить Грубая оценка кардинальности самостоятельно).

Если вы получаете проблему с запросами с одним словом, этот отрывок из связанной статьи может применяться.

В полнотекстовом поиске SQL Server 2008 у нас есть возможность изменить план, который генерируется на основе оценки мощности используемого поискового термина. Если план запроса фиксирован (как в параметризованном запросе внутри хранимой процедуры), этот шаг не состоится. Поэтому скомпилированный план всегда обслуживает этот запрос, даже если этот план не идеален для данного поискового запроса.

Так что вам может понадобиться использовать опцию RECOMPILE.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...