Wyszukiwanie pełnotekstowe powoduje dużą ilość czasu poświęconą na „inicjalizację FULLTEXT”

12

Obecnie próbuję uruchomić zapytania dotyczące zrzutu danych komentarzy przepełnienia stosu. Oto jak wygląda schemat:

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

Uruchomiłem to zapytanie względem tabeli i działało ono niezwykle wolno (ma 29 milionów wierszy, ale ma indeks pełnotekstowy):

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

Więc profilowałem to, czego rezultatem są:

|| 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 ||

Jak widać, inicjowanie FULLTEXT zajmuje dużo czasu. Czy to normalne? Jeśli nie, jak bym to naprawić?

hichris123
źródło
Pomysł: zbuduj drugi stół, w którym umieścisz co 1000 komentarzy w jednym polu tekstowym. Teraz najpierw wyszukujesz w tej drugiej tabeli i dostajesz na przykład id_group 2i id_group 23. Dzięki temu Twoje wyszukiwanie w głównej tabeli i ograniczenie zapytania do zakresów identyfikatorów od 2.000 do 2.999 i 23.000 do 23.999. Oczywiście 2. miejsce przyniesie więcej wyników w miarę potrzeb, gdy pomieszasz wszystkie komentarze, tworząc nowe kombinacje słów kluczowych, ale w końcu powinno to przyspieszyć całość. Oczywiście podwaja wykorzystanie miejsca na dysku. Nowe komentarze powinny być ZAWSZE w tabeli grup.
mgutt

Odpowiedzi:

5

Inni uznali tę sytuację za kłopotliwą

Ponieważ dokumentacja MySQL jest bardzo zwięzła w tym stanie wątku

Inicjalizacja FULLTEXT

Serwer przygotowuje się do wyszukiwania pełnotekstowego w języku naturalnym.

jedynym wyjściem będzie przygotowanie się z mniejszą ilością danych. W jaki sposób ?

SUGESTIA # 1

Spójrz ponownie na zapytanie. Zaznacza wszystkie kolumny. Dokonałbym refaktoryzacji zapytania, aby zebrać tylko kolumny id socomments. Następnie dołącz odzyskane identyfikatory z powrotem do socommentstabeli.

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

Może to stworzyć brzydszy plan WYJAŚNIJ, ale myślę, że profilowanie zmieni się na lepsze. Podstawowa idea jest taka: jeśli masz agresywne wyszukiwanie PEŁNOTEKSTOWE, spraw, aby gromadziło najmniej danych w tej FULLTEXT initializationfazie, skracając w ten sposób czas.

Polecałem to wiele razy wcześniej

SUGESTIA # 2

Upewnij się, że ustawiasz opcje FULLTEXT-a oparte na InnoDB, a nie te dla MyISAM. Dwie opcje, którymi powinieneś się martwić to:

Pomyśl o tym przez chwilę. Pole tekstowe to VARCHAR (600). Powiedzmy, że średnia to 300 bajtów. Masz ich 29 000 000 milionów. To by było trochę 8 GB. Być może zwiększenie innodb_ft_cache_size i innodb_ft_total_cache_size może również pomóc.

Upewnij się, że masz wystarczającą ilość pamięci RAM dla większych buforów InnoDB FULLTEXT.

SPRÓBUJ !!!

RolandoMySQLDBA
źródło
Wypróbował obie sugestie, skrócił czas około 10 sekund do 200 sekund. Dziwne jest to, że pula buforów jest wykorzystywana tylko w 9% ...
hichris123
Spróbuj umieścić znak plus w części PRZECIW: SELECT B.* FROM (SELECT id FROM socomments WHERE MATCH (Text) AGAINST ('+"fixed the post"' IN BOOLEAN MODE)) A LEFT JOIN socomments B USING (id);i sprawdź, czy to robi różnicę.
RolandoMySQLDBA
Dlaczego zaproponowałem znak plus? Doc ( dev.mysql.com/doc/refman/5.6/en/fulltext-boolean.html ) mówi: 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.W twoim konkretnym przypadku fixed the postmusi istnieć dokładna fraza .
RolandoMySQLDBA
Te same wyniki. Trochę szybszy i wolniejszy, więc prawdopodobnie z powodu drobnych różnic w czasie jego wykonania.
hichris123
5

Jeśli korzystasz z indeksów InnoDB FULLTEXT, zapytania często zawieszają się w stanie „Inicjalizacja FULLTEXT”, jeśli wysyłasz zapytania do tabeli z dużą liczbą usuniętych wierszy. W implementacji FULLTEXT firmy InnoDB usunięte wiersze nie są przycinane, dopóki nie zostanie uruchomiona kolejna operacja OPTIMIZE w odniesieniu do tabeli, której dotyczy problem. Zobacz: https://dev.mysql.com/doc/refman/5.6/en/innodb-fulltext-index.html

Aby usunąć wpisy indeksu pełnotekstowego dla usuniętych rekordów, należy uruchomić OPTYMALIZACJĘ TABELI w indeksowanej tabeli z innodb_optimize_fulltext_only = ON, aby odbudować indeks pełnotekstowy.

Można również sprawdzić liczbę usuniętych, ale nie wyczyszczonych rekordów, sprawdzając informacje_schema.innodb_ft_deleted

Aby rozwiązać ten problem, należy regularnie uruchamiać OPTYMALIZACJĘ TABELI dla tabel z indeksami InnoDB FULLTEXT.

Tyler
źródło
Rozumiem logikę, ale czy możesz to sprawdzić, innodb_optimize_fulltext_only=1a OPTIMIZEtabela faktycznie zajmuje się usuniętymi wierszami „w oczekiwaniu”? dba.stackexchange.com/questions/174486/…
Riedsio
0

Indeksy pełnotekstowe w MySQL nie są zaprojektowane do obsługi dużych ilości danych, więc szybkość wyszukiwania spada dość szybko, gdy Twój zbiór danych rośnie. Jednym z rozwiązań jest użycie zewnętrznych wyszukiwarek pełnotekstowych, takich jak Solr lub Sphinx, które mają ulepszoną funkcjonalność wyszukiwania (dostrajanie trafności i wsparcie wyszukiwania wyrażeń, wbudowane aspekty, fragmenty itp.) Rozszerzona składnia zapytań i znacznie szybsza prędkość od średniej do średniej -duże zestawy danych.

Solr jest oparty na platformie Java, więc jeśli uruchomisz aplikację opartą na Javie, będzie to dla ciebie naturalny wybór, Sphinx jest napisany w C ++ i działa jako demon w taki sam sposób jak MySQL. Jak tylko podasz silnikowi zewnętrznemu dane, które chcesz przeszukać, możesz również przenieść niektóre zapytania z MySQL. Nie mogę powiedzieć, który silnik jest lepszy w twoim przypadku, używam głównie Sphinx, a oto przykład użycia: http://astellar.com/2011/12/replacing-mysql-full-text-search-with-sphinx/

vfedorkov
źródło