首页 > 解决方案 > 全文搜索 Innodb 失败,MyIsam 返回结果

问题描述

我已经将一张桌子从 升级myisam到 ,innodb但性能不一样。当应该有某种关系时innodb返回一个分数。0myisam表返回相同术语的匹配项(我保留了旧表的副本,因此我仍然可以运行相同的查询)。

SELECT MATCH (COLUMNS) AGAINST ('+"Term Ex"' IN BOOLEAN MODE) as score
FROM table_myisam
where id = 1;

回报:

+-------+
| score |
+-------+
|     1 |
+-------+

但:

SELECT MATCH (COLUMNS) AGAINST ('+"Term Ex"' IN BOOLEAN MODE) as score
FROM table
where id = 1;

返回:

+-------+
| score |
+-------+
|     0 |
+-------+

我认为ex可能没有被索引,因为innodb_ft_min_token_size设置为3. 我将其降低1并优化了表格,但这没有影响。列内容的长度为 99 个字符,因此我假设整个列没有被索引,因为innodb_ft_max_token_size. 我也增加了它并150再次运行优化但再次得到相同的结果。

这些表之间的唯一区别是引擎和字符集。这张桌子正在使用utf8,这张桌子myisam正在使用latin1

有没有人看到这些行为,或者对如何解决它有建议?

更新: 我添加ft_stopword_file=""到我的my.cnfOPTIMIZE TABLE table再次运行。这次我得到了

优化 | 注意 | 表不支持优化,改为重新创建+分析

查询在此更改后有效。Ex虽然不是停用词,但不确定为什么会有所作为。

一个失败的新查询是:

SELECT MATCH (Columns) AGAINST ('+Term +Ex +in' IN BOOLEAN MODE) as score FROM Table where id = 1;

+-------+
| score |
+-------+
|     0 |
+-------+

in导致失败,但这是我表中的下一个词。

SELECT MATCH (Columns) AGAINST ('+Term +Ex' IN BOOLEAN MODE) as score FROM Table where id = 1;

+--------------------+
| score              |
+--------------------+
| 219.30206298828125 |
+--------------------+

我也试过了CREATE TABLE my_stopwords(value VARCHAR(30)) ENGINE = INNODB;,然后更新my.cnfinnodb_ft_server_stopword_table='db/my_stopwords'。我重新启动并运行:

show variables like 'innodb_ft_server_stopword_table';

这带回了:

+---------------------------------+---------------------------+
| Variable_name                   | Value                     |
+---------------------------------+---------------------------+
| innodb_ft_server_stopword_table | 'db/my_stopwords'; |
+---------------------------------+---------------------------+

所以我认为in现在不会导致查询失败,但它会继续。我也OPTIMIZE TABLE table再试了一次,甚至ALTER TABLE table DROP INDEX ...ALTER TABLE table ADD FULLTEXT KEY ...没有影响。

第二次更新 问题在于停用词。

$userinput = preg_replace('/\b(a|about|an|are|as|at|be|by|com|de|en|for|from|how|i|in|is|it|la|of|on|or|that|the|this|to|was|what|when|where|who|will|with|und|the|www)\b/', '', $userinput);

解决了这个问题,但这对我来说似乎不是一个好的解决方案。我想要一个避免在 mysql 中出现停用词的解决方案。

停用词表数据:

CREATE TABLE `my_stopwords` (
  `value` varchar(30) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1

Name: my_stopwords
         Engine: InnoDB
        Version: 10
     Row_format: Compact
           Rows: 0
 Avg_row_length: 0
    Data_length: 16384
Max_data_length: 0
   Index_length: 0
      Data_free: 0
 Auto_increment: NULL
    Create_time: 2019-04-09 17:39:55
    Update_time: NULL
     Check_time: NULL
      Collation: latin1_swedish_ci
       Checksum: NULL
 Create_options: 
        Comment: 

标签: mysqlfull-text-searchinnodbmyisammysql-5.6

解决方案


MyISAM 的 FULLTEXT 和 InnoDB 之间有几个不同之处。我认为您被“短”词和/或停用词的处理所吸引。MyISAM 将显示行,但 InnoDB 将失败。

我在使用 FT 时(以及在切换到 InnoDB 之后)所做的是过滤用户的输入以避免简短的单词。这需要额外的努力,但可以让我得到想要的行。我的情况略有不同,因为结果查询是这样的。请注意,我已经添加+了要求单词,但不是短于 3 的单词(我ft_min_token_size的是 3)。这些搜索是针对build a tablebuild the table

WHERE match(description) AGAINST('+build* a +table*' IN BOOLEAN MODE)
WHERE match(description) AGAINST('+build* +the* +table*' IN BOOLEAN MODE)

(尾随*可能是多余的;我没有对此进行调查。)

另一种方法

由于 FT 在非短、非停用词方面非常有效,因此分两个阶段进行搜索,每个阶段都是可选的:要搜索“长词”,请执行

WHERE MATCH(d) AGAINST ('+long +word' IN BOOLEAN MODE)
  AND d REGEXP '[[:<:]]a[[:>:]]'

第一部分通过查找“long”和“word”(作为words)快速减少可能的行。第二部分确保字符串中也有一个单词 a。这REGEXP是昂贵的,但只会应用于通过第一次测试的那些行。

搜索“长词”:

WHERE MATCH(d) AGAINST ('+long +word' IN BOOLEAN MODE)

搜索单词“a”:

WHERE d REGEXP '[[:<:]]a[[:>:]]'

警告:这种情况会很慢。

注意:我的示例允许单词按任何顺序排列,并且位于字符串中的任何位置。也就是说,这个字符串在我所有的例子中都会匹配:“她想从他那里得到一句话。”


推荐阅读