Полнотекстовый поиск приводит к большому количеству времени, потраченному на 'инициализацию FULLTEXT'

12

В настоящее время я пытаюсь выполнить несколько запросов к дампу данных комментариев переполнения стека. Вот как выглядит схема:

CREATE TABLE `socomments` (
  `Id` int(11) NOT NULL,
  `PostId` int(11) NOT NULL,
  `Score` int(11) DEFAULT NULL,
  `Text` varchar(600) NOT NULL,
  `CreationDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `UserId` int(11) NOT NULL,
  PRIMARY KEY (`Id`),
  KEY `idx_socomments_PostId` (`PostId`),
  KEY `CreationDate` (`CreationDate`),
  FULLTEXT KEY `Text` (`Text`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

Я выполнил этот запрос к таблице, и он работал невероятно медленно (у него есть 29 миллионов строк, но у него есть полнотекстовый индекс):

SELECT *
FROM socomments
WHERE MATCH (Text) AGAINST ('"fixed the post"' IN BOOLEAN MODE)

Итак, я его профилировал, результаты которого таковы:

|| Status                     || Duration ||
|| starting                   || 0.000058 ||
|| checking permissions       || 0.000006 ||
|| Opening tables             || 0.000014 ||
|| init                       || 0.000019 ||
|| System lock                || 0.000006 ||
|| optimizing                 || 0.000007 ||
|| statistics                 || 0.000013 ||
|| preparing                  || 0.000005 ||
|| FULLTEXT initialization    || 207.1112 ||
|| executing                  || 0.000009 ||
|| Sending data               || 0.000856 ||
|| end                        || 0.000004 ||
|| query end                  || 0.000004 ||
|| closing tables             || 0.000006 ||
|| freeing items              || 0.000059 ||
|| logging slow query         || 0.000037 ||
|| cleaning up                || 0.000046 ||

Как видите, он долго тратит на инициализацию FULLTEXT. Это нормально? Если нет, то как бы это исправить?

hichris123
источник
Идея: Постройте вторую таблицу, где вы помещаете каждые 1000 комментариев в одно текстовое поле. Теперь вы ищете сначала в этой второй таблице, и вы получите, например, id_group 2и id_group 23. С этим ваш поиск внутри вашей основной таблицы и ограничить ваш запрос диапазонами идентификаторов от 2.000 до 2.999 и от 23.000 до 23.999. Конечно, 2-й будет давать больше результатов по мере необходимости, поскольку вы смешиваете все комментарии, создавая новые комбинации ключевых слов, но, наконец, это должно ускорить все это. Конечно, это удваивает использование дискового пространства. Новые комментарии должны быть связаны с таблицей групп.
mgutt

Ответы:

5

Другие нашли эту неприятную ситуацию

Поскольку документация MySQL очень краткая в этом состоянии потока

FULLTEXT инициализация

Сервер готовится выполнить полнотекстовый поиск на естественном языке.

ваш единственный выход - сделать подготовку с меньшим количеством данных. Как ?

ПРЕДЛОЖЕНИЕ № 1

Посмотрите на ваш запрос еще раз. Он выбирает все столбцы. Я бы рефакторинг запроса, чтобы собрать только столбцы id из socomments. Затем присоедините полученные идентификаторы к socommentsтаблице.

SELECT B.* FROM
(SELECT id FROM socomments
WHERE MATCH (Text) AGAINST ('"fixed the post"' IN BOOLEAN MODE)) A
LEFT JOIN socomments B USING (id);

Это может привести к уродливому плану EXPLAIN, но я думаю, что профилирование изменится к лучшему. Основная идея такова: если у вас агрессивный поиск FULLTEXT, сделайте так, чтобы он собирал наименьшее количество данных на этом FULLTEXT initializationэтапе, что сокращает время.

Я рекомендовал это много раз, прежде чем

ПРЕДЛОЖЕНИЕ № 2

Убедитесь, что вы устанавливаете параметры FULLTEXT на основе InnoDB, а не параметры MyISAM. Вы должны быть обеспокоены двумя вариантами:

  • innodb_ft_cache_size
    • Значение по умолчанию 8000000 (7,629M)
    • Макс. Значение 80000000 (76,29M)
  • innodb_ft_total_cache_size
    • Значение по умолчанию 640000000 (610M)
    • Макс. Значение 1600000000 (1525M = 1,49G)

Задумайтесь об этом на мгновение. Текстовое поле VARCHAR (600). Скажем, среднее значение составляет 300 байт. У вас есть 29 000 000 миллионов из них. Это было бы, что было бы немного 8 ГБ. Возможно, также могут помочь увеличение innodb_ft_cache_size и innodb_ft_total_cache_size .

Убедитесь, что у вас достаточно оперативной памяти для больших буферов InnoDB FULLTEXT.

ДАЙТЕ ЭТО ПОПРОБУЙТЕ !!!

RolandoMySQLDBA
источник
Опробовав оба предложения, он сократил время с 10 до 200 секунд. Странно то, что буферный пул
загружен
Попробуйте поставить знак «плюс» внутри ПРОТИВ части: SELECT B.* FROM (SELECT id FROM socomments WHERE MATCH (Text) AGAINST ('+"fixed the post"' IN BOOLEAN MODE)) A LEFT JOIN socomments B USING (id);и посмотрите, имеет ли это значение.
RolandoMySQLDBA
Причина, по которой я предложил знак плюс? Док ( dev.mysql.com/doc/refman/5.6/en/fulltext-boolean.html ) говорит, что A leading or trailing plus sign indicates that this word must be present in each row that is returned. InnoDB only supports leading plus signs.в вашем конкретном случае fixed the postдолжна существовать точная фраза .
RolandoMySQLDBA
Те же результаты. Немного быстрее и медленнее, поэтому, вероятно, из-за незначительных различий в том, когда он был выполнен.
hichris123
5

Если вы используете индексы InnoDB FULLTEXT, запросы часто зависают в состоянии «инициализация FULLTEXT», если вы выполняете запросы к таблице с большим количеством удаленных строк. В реализации InnoDB FULLTEXT удаленные строки не удаляются до тех пор, пока не будет запущена последующая операция OPTIMIZE для уязвимой таблицы. См .: https://dev.mysql.com/doc/refman/5.6/en/innodb-fulltext-index.html.

Чтобы удалить записи полнотекстового индекса для удаленных записей, необходимо запустить OPTIMIZE TABLE для индексированной таблицы с innodb_optimize_fulltext_only = ON, чтобы перестроить полнотекстовый индекс.

Можно также проверить количество удаленных, но не очищенных записей, запросив информацию_schema.innodb_ft_deleted

Для решения этой проблемы следует регулярно запускать OPTIMIZE TABLE для таблиц с индексами InnoDB FULLTEXT.

Тайлер
источник
Я понял логику этого, но можете ли вы проверить это, innodb_optimize_fulltext_only=1и OPTIMIZEтаблица действительно заботится об удаленных строках «в ожидании»? dba.stackexchange.com/questions/174486/…
Riedsio
0

Полнотекстовые индексы в MySQL не предназначены для поддержки больших объемов данных, поэтому скорость поиска снижается довольно быстро по мере роста набора данных. Одним из решений является использование внешних полнотекстовых поисковых систем, таких как Solr или Sphinx, которые имеют улучшенные функциональные возможности поиска (настройка релевантности и поддержка поиска по фразам, встроенные фасеты, фрагменты и т. Д.), Расширенный синтаксис запросов и значительно более высокую скорость в середине-до -большой набор данных.

Solr основан на платформе Java, поэтому, если вы запускаете приложение на основе Java, это будет естественным выбором для вас, Sphinx написан на C ++ и действует как демон в той же манере, что и MySQL. Как только вы наполняете внешний движок данными, которые вы хотите найти, вы также можете удалить некоторые запросы из MySQL. Я не могу сказать вам, какой движок лучше в вашем случае, я использую в основном Sphinx, и вот пример использования: http://astellar.com/2011/12/replacing-mysql-full-text-search-with-sphinx/

vfedorkov
источник