Медленный полнотекстовый поиск MySQL - PullRequest
3 голосов
/ 03 октября 2010

Я использую этот запрос для выполнения полнотекстового поиска в базе данных MySQL:

SELECT DISTINCT 
questions.id, 
questions.uniquecode, 
questions.spam,
questions.questiondate,
questions.userid,
questions.description,
users.login AS username,
questions.questiontext,
questions.totalvotes,
MATCH(questions.questiontext, questions.uniquecode) 
AGAINST ('rock guitarist chick*' IN BOOLEAN MODE) AS relevance 

FROM questions 

LEFT JOIN users ON questions.userid = users.id 
LEFT JOIN answer_mapping ON questions.id = answer_mapping.questionid 
LEFT JOIN answers ON answer_mapping.answerid = answers.id
LEFT JOIN tagmapping ON questions.id = tagmapping.questionid
LEFT JOIN tags ON tagmapping.tagid = tags.id 

WHERE questions.spam < 10 

AND 

(
  MATCH(questions.questiontext, questions.uniquecode) 
  AGAINST ('rock guitarist chick*' IN BOOLEAN MODE) 

OR MATCH(answers.answertext) AGAINST ('rock guitarist chick*' IN BOOLEAN MODE) 

OR MATCH (tags.tag) AGAINST ('rock guitarist chick*' IN BOOLEAN MODE)

) GROUP BY questions.id ORDER BY relevance DESC

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

Статистика таблицы:

вопросы - 400 записей

индексы

  • ПЕРВИЧНЫЕBTREE - идентификатор
  • BTREE - уникальный код
  • BTREE - дата запроса
  • BTREE - идентификатор пользователя
  • FULLTEXT - текст вопроса
  • FULLTEXT - уникальный код

ответы - 3635 записей

индексы

  • PRIMARY - BTREE - id
  • BTREE - ответная дата
  • BTREE - вопросительный
  • FULLTEXT - answertext

answer_mapping - 4 228 записей

индексы

  • ПЕРВИЧНЫЙ - BTREE - идентификатор
  • BTREE - answerid
  • BTREE - вопросный
  • BTREE - идентификатор пользователя

теги -1847 записей

индексы

  • ПЕРВИЧНЫЕ - BTREE - id
  • BTREE - тег
  • FULLTEXT - тег

отображение тегов - 3389 записей

индексы

  • PRIMARY - BTREE - id
  • BTREE- tagid
  • BTREE - questionid

По какой-либо причине, когда я удаляю теги и , СОЕДИНЯЕТСЯ, поиск значительно ускоряется.

Есть ли у вас какие-либо советы о том, как ускорить этот запрос?

Заранее спасибо!

Ответы [ 3 ]

1 голос
/ 03 октября 2010

хорошо, вы можете объединить ваше объединение в кэшированное представление или дополнительную таблицу или что-то в этом роде.активируйте кэш запросов и определите соединение как выборку, чтобы его можно было кэшировать.обеспечить достаточно памяти и т. д., но это не должно быть узким местом.ну наверное в твоем случае это потому что ... только 400 записей?это ничего ... и уже медленно?потому что остальное выглядит хорошо.на каком оборудовании / конфигурации вы работаете?

но я думаю, что это неправильный подход.MySQL не предназначен для этого.на самом деле полнотекстовая функция ограничена myisam.

вам следует рассмотреть возможность использования lucene / solr с помощью обработчика запросов dismax.он должен дать вам хорошие результаты примерно через 50-100 мс с индексом нескольких тысяч документов.в какой-то момент вы можете осквернить его, чтобы количество записей практически не ограничивалось.Кроме того, у вас есть лучшие варианты и вы можете достичь лучших результатов.например, выполнять нечеткое сопоставление или придавать больший вес новым документам или иметь теги, более релевантные, чем заголовок, выполнять анализ после запроса, фасетирование и т. д. *

0 голосов
/ 14 февраля 2017

Ваша формулировка запроса работает медленно по нескольким причинам, но я не уверен в деталях. Пожалуйста, предоставьте EXPLAIN FORMAT=JSON SELECT ... для дальнейшего обсуждения.

А пока давайте перепишем запрос так, чтобы он работал быстрее. (И это может избавить от ошибки, с которой вы еще не сталкивались.)

Во-первых, давайте создадим отладку этого. Он выполняет 3 FT поиска в 3 отдельных запросах, а затем объединяет (UNION) всего question_ids от каждого.

    ( SELECT question_id,
         MATCH (... ) as relevance
         FROM questions
         WHERE MATCH (questiontext, ...) AGAINST ... )
    UNION ALL
    ( SELECT am.question_id,
         MATCH (... ) as relevance
         FROM answers AS a
         JOIN answer_mapping AS am ON am.answerid = a.id
         WHERE MATCH (a.answertext) AGAINST ... )
    UNION ALL
    ( SELECT tm.question_id,
         MATCH (... ) as relevance
         FROM tags AS t
         JOIN tagsmapping tm ON ...
         WHERE MATCH (t.tag) AGAINST ... )

Обратите внимание, как каждый подзапрос предназначен для , начиная с таблицы с индексом FT и заканчивая question_id.

Теперь промежуточный запрос:

SELECT question_id,
         MAX(relevance)  -- (this fixes the unseen bug)
    FROM ( that query ) AS q1
    GROUP BY question_id
    ORDER BY relevance DESC  -- optional; needed for `LIMIT`
    LIMIT 20          -- to limit the rows, do it at this stage

Если это работает достаточно быстро и дает «правильное» question_ids, тогда мы можем продолжить ...

Используйте это как подзапрос, чтобы получить доступ к остальным данным:

SELECT .... -- the `questions` fields, using `q....`,
       ( SELECT login FROM users WHERE q.userid = id ) AS username
    FROM ( the intermediate query ) AS q2
    JOIN questions AS q
    questions q.spam < 10 
    ORDER BY q2.relevance

Да, это JOINing назад к questions, но это оказывается быстрее.

Обратите внимание, что GROUP BY здесь не требуется. И, если внутренний запрос имеет LIMIT, он здесь не понадобится.

Я прошу прощения, если я не совсем все понял правильно; было больше преобразований, чем я ожидал.

0 голосов
/ 09 февраля 2017

Вы также можете попробовать запустить OPTIMIZE TABLE questions

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

См. Ссылку: https://dev.mysql.com/doc/refman/5.7/en/fulltext-fine-tuning.html

...