A pesquisa de texto completo resulta em uma grande quantidade de tempo gasto na 'inicialização do FULLTEXT'

12

Atualmente, estou tentando executar algumas consultas em um despejo de dados dos comentários do Stack Overflow. Aqui está a aparência do esquema:

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

Fiz essa consulta na tabela e ela ficou incrivelmente lenta (ela possui 29 milhões de linhas, mas possui um índice de texto completo):

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

Então eu criei um perfil, cujos resultados são:

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

Como você pode ver, ele passa muito tempo na inicialização do FULLTEXT. Isso é normal? Se não, como eu consertaria isso?

hichris123
fonte
Ideia: Crie uma 2ª tabela onde você coloca cada 1.000 comentários em um campo de texto. Agora você pesquisa primeiro nesta segunda tabela e obtém, por exemplo, id_group 2e id_group 23. Com isso, sua pesquisa dentro da tabela principal e limita sua consulta aos intervalos de ID 2.000 a 2.999 e 23.000 a 23.999. É claro que o segundo resultará em mais resultados, conforme necessário, à medida que você mistura todos os comentários, criando novas combinações de palavras-chave, mas, finalmente, deve acelerar a coisa toda. É claro que duplica o uso do espaço em disco. Novos comentários devem ser CONCATADOS na tabela de grupo.
mgutt

Respostas:

5

Outros acharam esta uma situação problemática

Como a documentação do MySQL é muito concisa neste estado de thread

Inicialização FULLTEXT

O servidor está se preparando para executar uma pesquisa de texto completo em idioma natural.

seu único recurso seria fazer a preparação com menos dados. Como ?

SUGESTÃO # 1

Olhe para sua consulta novamente. Está selecionando todas as colunas. Eu refatoraria a consulta para coletar apenas as colunas de identificação socomments. Em seguida, junte os IDs recuperados de volta à socommentstabela.

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

Isso pode produzir um plano EXPLAIN mais feio, mas acho que os perfis mudarão para melhor. A idéia básica é: se você tem uma pesquisa FULLTEXT agressiva, faça com que ela colete a menor quantidade de dados durante essa FULLTEXT initializationfase, reduzindo assim o tempo.

Eu recomendei isso muitas vezes antes

SUGESTÃO # 2

Certifique-se de definir as opções FULLTEXT baseadas no InnoDB, não as do MyISAM. As duas opções que você deve se preocupar são

Pense nisso por um momento. O campo de texto é VARCHAR (600). Digamos que a média é de 300 bytes. Você tem 29.000.000 milhões deles. Isso seria um pouco de 8GB. Talvez aumentar innodb_ft_cache_size e innodb_ft_total_cache_size também possa ajudar.

Verifique se você possui RAM suficiente para buffers InnoDB FULLTEXT maiores.

DE UMA CHANCE !!!

RolandoMySQLDBA
fonte
Tentei ambas as sugestões, reduziu o tempo em torno de 10 segundos, para 200 segundos. Estranho é que o pool de buffer é apenas com 9% de utilização ...
hichris123
Tente colocar um sinal de mais na parte CONTRA: SELECT B.* FROM (SELECT id FROM socomments WHERE MATCH (Text) AGAINST ('+"fixed the post"' IN BOOLEAN MODE)) A LEFT JOIN socomments B USING (id);e veja se isso faz diferença.
RolandoMySQLDBA
A razão pela qual sugeri um sinal de mais? Doc ( dev.mysql.com/doc/refman/5.6/en/fulltext-boolean.html ) diz 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.No seu caso particular, a frase exata fixed the postdeve existir.
RolandoMySQLDBA
Mesmos resultados. Um pouco mais rápido e mais lento, provavelmente apenas devido a pequenas diferenças em quando foi executado.
hichris123
5

Se você estiver usando os índices InnoDB FULLTEXT, as consultas geralmente serão interrompidas no estado "Inicialização FULLTEXT" se você estiver consultando uma tabela que possui um grande número de linhas excluídas. Na implementação FULLTEXT do InnoDB, as linhas excluídas não são removidas até que uma operação OPTIMIZE subsequente seja executada na tabela afetada. Consulte: https://dev.mysql.com/doc/refman/5.6/en/innodb-fulltext-index.html

Para remover entradas de índice de texto completo para registros excluídos, você deve executar OPTIMIZE TABLE na tabela indexada com innodb_optimize_fulltext_only = ON para reconstruir o índice de texto completo.

Pode-se também inspecionar o número de registros excluídos, mas não eliminados, consultando information_schema.innodb_ft_deleted

Para resolver isso, deve-se executar regularmente OPTIMIZE TABLE em tabelas com índices InnoDB FULLTEXT.

Tyler
fonte
Eu entendo a lógica disso, mas você pode verificar isso innodb_optimize_fulltext_only=1e uma OPTIMIZEtabela realmente cuida das linhas excluídas "em espera"? dba.stackexchange.com/questions/174486/…
Riedsio
0

Os índices de texto completo no MySQL não foram projetados para suportar grandes quantidades de dados, portanto, a velocidade da pesquisa diminui rapidamente, à medida que seu conjunto de dados cresce. Uma das soluções é usar mecanismos externos de pesquisa de texto completo, como o Solr ou o Sphinx, que melhoraram a funcionalidade de pesquisa (ajuste de relevância e suporte à pesquisa de frases, facetas internas, trechos, etc.) sintaxe de consulta estendida e velocidade muito mais rápida em meados de conjuntos de dados grandes.

O Solr é baseado na plataforma Java, portanto, se você executar aplicativos baseados em Java, será uma escolha natural para você, o Sphinx será escrito em C ++ e atuará como um daemon da mesma maneira que o MySQL. Assim que você alimentar o mecanismo externo com os dados que deseja pesquisar, você também poderá mover algumas consultas para fora do MySQL. Não sei dizer qual mecanismo é melhor no seu caso, uso principalmente o Sphinx e aqui está um exemplo de uso: http://astellar.com/2011/12/replacing-mysql-full-text-search-with-sphinx/

vfedorkov
fonte