Повний текстовий пошук призводить до великої кількості часу, проведеного в ініціалізації FULLTEXT


12

Наразі я намагаюся запустити деякі запити щодо дамп даних коментарів Stack Overflow. Ось як виглядає схема:

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. Це нормально? Якщо ні, то як би я це виправити?


Ідея: Створіть другу таблицю, куди ви помістите кожні 1.000 коментарів в одне текстове поле. Тепер ви спочатку шукаєте в цій другій таблиці і отримуєте, наприклад, 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,629 млн.)
    • Максимальне значення 80000000 (76,29 млн)
  • innodb_ft_total_cache_size
    • Значення за замовчуванням 640000000 (610 млн.)
    • Максимальне значення 1600000000 (1525M = 1,49G)

Подумайте про це на мить. Текстове поле - VARCHAR (600). Скажімо, середнє значення - 300 байт. У вас їх 29 000 000 мільйонів. Це було б трохи 8 Гб. Можливо, збільшення innodb_ft_cache_size та innodb_ft_total_cache_size також може допомогти.

Переконайтеся, що у вас є достатня кількість оперативної пам’яті для більших буферів InnoDB FULLTEXT.

СПРОБУВАТИ !!!


Спробувавши обидві пропозиції, це скоротило час приблизно на 10 секунд, до 200 секунд. Дивно те, що буферний пул використовується лише на 9% використання ...
hichris123

Спробуйте поставити знак плюс всередині частини ПРОТИ: 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

Причина, яку я запропонував знак плюс? Doc ( 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 ініціалізація", якщо ви запитуєте проти таблиці, що має велику кількість видалених рядків. У реалізації FULLTEXT InnoDB видалені рядки не обрізаються, поки наступна операція OPTIMIZE не буде запущена проти постраждалої таблиці. Дивіться: https://dev.mysql.com/doc/refman/5.6/en/innodb-fulltext-index.html

Щоб видалити повнотекстові записи індексу для видалених записів, потрібно запустити OPTIMIZE TABLE в індексованій таблиці з innodb_optimize_fulltext_only = ON, щоб відновити повнотекстовий індекс.

Можна також перевірити кількість видалених, але не очищених записів, запитуючи information_schema.innodb_ft_deleted

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


Я отримую логіку на цьому, але ви можете переконатися , що innodb_optimize_fulltext_only=1і OPTIMIZEтаблиця на справді дбає про віддалених рядків «в очікуванні»? dba.stackexchange.com/questions/174486/…
Рієдіо


0

Повнотекстові індекси в MySQL не розроблені для підтримки великої кількості даних, тому швидкість пошуку знижується досить швидко, оскільки ваш набір даних збільшується. Одне з рішень полягає у використанні зовнішніх повнотекстових пошукових систем, таких як Solr або Sphinx, які мають покращену функціональність пошуку (відповідність налаштування релевантності та пошук фрази, вбудовані грані, фрагменти тощо), розширений синтаксис запитів та набагато більш швидку швидкість від середини до -великі набори даних

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.