Bei der Volltextsuche wird viel Zeit für die 'FULLTEXT-Initialisierung' aufgewendet.

12

Ich versuche derzeit, einige Abfragen für einen Daten-Dump der Kommentare von Stack Overflow auszuführen. So sieht das Schema aus:

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

Ich habe diese Abfrage für die Tabelle ausgeführt und sie ist unglaublich langsam (29 Millionen Zeilen, aber ein Volltextindex):

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

Also habe ich es profiliert. Die Ergebnisse sind:

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

Wie Sie sehen, dauert die FULLTEXT-Initialisierung sehr lange. Ist das normal? Wenn nicht, wie würde ich das beheben?

hichris123
quelle
Idee: Erstellen Sie eine zweite Tabelle, in der Sie alle 1.000 Kommentare in ein Textfeld eingeben. Jetzt suchen Sie zuerst in dieser zweiten Tabelle und erhalten zum Beispiel id_group 2und id_group 23. Mit dieser Option durchsuchen Sie Ihre Haupttabelle und begrenzen Ihre Abfrage auf die ID-Bereiche 2.000 bis 2.999 und 23.000 bis 23.999. Natürlich führt die zweite zu mehr Ergebnissen, wenn Sie alle Kommentare verwechseln und neue Schlüsselwortkombinationen erstellen, aber letztendlich sollte dies die ganze Sache beschleunigen. Natürlich verdoppelt es den Speicherplatzbedarf. Neue Kommentare sollten der Gruppentabelle hinzugefügt werden .
mgutt

Antworten:

5

Andere haben dies als problematisch empfunden

Da die MySQL-Dokumentation über diesen Thread-Status sehr knapp ist

FULLTEXT-Initialisierung

Der Server bereitet eine Volltextsuche in natürlicher Sprache vor.

Sie können sich nur auf weniger Daten vorbereiten. Wie ?

VORSCHLAG # 1

Schauen Sie sich Ihre Anfrage noch einmal an. Es werden alle Spalten ausgewählt. Ich würde die Abfrage umgestalten, um nur die ID-Spalten von zu sammeln socomments. Verbinden Sie dann diese abgerufenen IDs wieder mit der socommentsTabelle.

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

Dies könnte zu einem hässlicheren EXPLAIN-Plan führen, aber ich denke, die Profilerstellung wird sich zum Guten wenden. Die Grundidee ist: Wenn Sie eine aggressive FULLTEXT-Suche verwenden, sollten Sie dafür sorgen, dass in dieser FULLTEXT initializationPhase die geringste Datenmenge erfasst wird, wodurch die Zeit verkürzt wird.

Ich habe das schon oft empfohlen

VORSCHLAG # 2

Bitte stellen Sie sicher, dass Sie die InnoDB-basierten FULLTEXT-Optionen einstellen, nicht die für MyISAM. Die zwei Optionen, die Sie betreffen sollten, sind

Denken Sie einen Moment darüber nach. Das Textfeld ist VARCHAR (600). Angenommen, der Durchschnitt liegt bei 300 Bytes. Sie haben 29.000.000.000 von ihnen. Das wäre ein bisschen 8GB. Vielleicht hilft es auch, innodb_ft_cache_size und innodb_ft_total_cache_size zu erhöhen .

Stellen Sie sicher, dass Sie über genügend RAM für größere InnoDB FULLTEXT-Puffer verfügen.

VERSUCHE ES !!!

RolandoMySQLDBA
quelle
Versuchte beide Vorschläge, senkte es die Zeit um 10 Sekunden auf 200 Sekunden. Merkwürdige Sache ist, dass der Pufferpool nur bei 9% Auslastung ist ...
hichris123
Setzen Sie ein Pluszeichen in das Feld GEGEN: SELECT B.* FROM (SELECT id FROM socomments WHERE MATCH (Text) AGAINST ('+"fixed the post"' IN BOOLEAN MODE)) A LEFT JOIN socomments B USING (id);und prüfen Sie, ob es einen Unterschied macht.
RolandoMySQLDBA
Der Grund, warum ich ein Pluszeichen vorgeschlagen habe? Doc ( dev.mysql.com/doc/refman/5.6/en/fulltext-boolean.html ) sagt 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.in Ihrem speziellen Fall die genaue Phrase fixed the postmuss vorhanden sein.
RolandoMySQLDBA
Gleiche Ergebnisse. Etwas schneller und langsamer, also wahrscheinlich nur aufgrund von winzigen Unterschieden bei der Ausführung.
hichris123
5

Wenn Sie InnoDB-FULLTEXT-Indizes verwenden, bleiben Abfragen häufig im Status "FULLTEXT-Initialisierung" hängen, wenn Sie eine Abfrage für eine Tabelle mit einer großen Anzahl gelöschter Zeilen durchführen. In der FULLTEXT-Implementierung von InnoDB werden gelöschte Zeilen erst gelöscht, wenn eine nachfolgende OPTIMIZE-Operation für die betroffene Tabelle ausgeführt wird. Siehe: https://dev.mysql.com/doc/refman/5.6/en/innodb-fulltext-index.html

Um Volltextindexeinträge für gelöschte Datensätze zu entfernen, müssen Sie OPTIMIZE TABLE für die indizierte Tabelle mit innodb_optimize_fulltext_only = ON ausführen, um den Volltextindex neu zu erstellen.

Sie können auch die Anzahl der gelöschten, aber nicht gelöschten Datensätze überprüfen, indem Sie information_schema.innodb_ft_deleted abfragen

Um dies zu beheben, sollten Sie regelmäßig OPTIMIZE TABLE für Tabellen mit InnoDB FULLTEXT-Indizes ausführen.

Tyler
quelle
Ich verstehe die Logik dazu, aber können Sie das überprüfen innodb_optimize_fulltext_only=1und eine OPTIMIZETabelle kümmert sich tatsächlich um die gelöschten Zeilen "in Wartestellung"? dba.stackexchange.com/questions/174486/…
Riedsio
0

Volltextindizes in MySQL unterstützen keine großen Datenmengen, sodass die Suchgeschwindigkeit mit zunehmender Datenmenge sehr schnell abnimmt. Eine der Lösungen besteht darin, externe Volltextsuchmaschinen wie Solr oder Sphinx zu verwenden, die über verbesserte Suchfunktionen (Unterstützung für Relevanzabstimmung und Phrasensuche, integrierte Facetten, Snippets usw.), erweiterte Abfragesyntax und eine wesentlich schnellere Suchgeschwindigkeit in der Mitte bis Mitte verfügen -große Datensätze.

Solr basiert auf der Java-Plattform. Wenn Sie also eine Java-basierte Anwendung ausführen, ist Sphinx in C ++ geschrieben und fungiert wie MySQL als Daemon. Sobald Sie eine externe Engine mit den Daten versorgen, die Sie durchsuchen möchten, können Sie auch einige Abfragen aus MySQL verschieben. Ich kann dir nicht sagen, welche Engine in deinem Fall besser ist, ich verwende hauptsächlich Sphinx und hier ist ein Anwendungsbeispiel: http://astellar.com/2011/12/replacing-mysql-full-text-search-with-sphinx/

vfedorkov
quelle