关于MSSQL 全文索引 某些词特别慢的问题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于MSSQL 全文索引 某些词特别慢的问题相关的知识,希望对你有一定的参考价值。
我做的全文索引,有一些词特别慢。比如 qq勇士 ,qq地下城,dnf勇士什么的都搜索的特别慢。甚至造成死锁。这个是怎么回事?
1. 执行计划中明明有使用到索引,为什么执行还是这么慢?2. 执行计划中显示扫描行数为 644,为什么 slow log 中显示 100 多万行?
a. 我们先看执行计划,选择的索引 “INDX_BIOM_ELOCK_TASK3(TASK_ID)”。结合 sql 来看,因为有 "ORDER BY TASK_ID DESC" 子句,排序通常很慢,如果使用了文件排序性能会更差,优化器选择这个索引避免了排序。
那为什么不选 possible_keys:INDX_BIOM_ELOCK_TASK 呢?原因也很简单,TASK_DATE 字段区分度太低了,走这个索引需要扫描的行数很大,而且还要进行额外的排序,优化器综合判断代价更大,所以就不选这个索引了。不过如果我们强制选择这个索引(用 force index 语法),会看到 SQL 执行速度更快少于 10s,那是因为优化器基于代价的原则并不等价于执行速度的快慢;
b. 再看执行计划中的 type:index,"index" 代表 “全索引扫描”,其实和全表扫描差不多,只是扫描的时候是按照索引次序进行而不是行,主要优点就是避免了排序,但是开销仍然非常大。
Extra:Using where 也意味着扫描完索引后还需要回表进行筛选。一般来说,得保证 type 至少达到 range 级别,最好能达到 ref。
在第 2 点中提到的“慢日志记录Rows_examined: 1161559,看起来是全表扫描”,这里更正为“全索引扫描”,扫描行数确实等于表的行数;
c. 关于执行计划中:“rows:644”,其实这个只是估算值,并不准确,我们分析慢 SQL 时判断准确的扫描行数应该以 slow log 中的 Rows_examined 为准。
4. 优化建议:添加组合索引 IDX_REL_DEVID_TASK_ID(REL_DEVID,TASK_ID)
优化过程:
TASK_DATE 字段存在索引,但是选择度很低,优化器不会走这个索引,建议后续可以删除这个索引:
select count(*),count(distinct TASK_DATE) from T_BIOMA_ELOCK_TASK;+------------+---------------------------+| count(*) | count(distinct TASK_DATE) |+------------+---------------------------+| 1161559 | 223 |+------------+---------------------------+
在这个 sql 中 REL_DEVID 字段从命名上看选择度较高,通过下面 sql 来检验确实如此:
select count(*),count(distinct REL_DEVID) from T_BIOMA_ELOCK_TASK;+----------+---------------------------+| count(*) | count(distinct REL_DEVID) |+----------+---------------------------+| 1161559 | 62235 |+----------+---------------------------+
由于有排序,所以得把 task_id 也加入到新建的索引中,REL_DEVID,task_id 组合选择度 100%:
select count(*),count(distinct REL_DEVID,task_id) from T_BIOMA_ELOCK_TASK;+----------+-----------------------------------+| count(*) | count(distinct REL_DEVID,task_id) |+----------+-----------------------------------+| 1161559 | 1161559 |+----------+-----------------------------------+
在测试环境添加 REL_DEVID,TASK_ID 组合索引,测试 sql 性能:alter table T_BIOMA_ELOCK_TASK add index idx_REL_DEVID_TASK_ID(REL_DEVID,TASK_ID);
添加索引后执行计划:
这里还要注意一点“隐式转换”:REL_DEVID 字段数据类型为 varchar,需要在 sql 中加引号:AND T.REL_DEVID = 000000025xxx >> AND T.REL_DEVID = '000000025xxx'
执行时间从 10s+ 降到 毫秒级别:
1 row in set (0.00 sec)
结论
一个典型的 order by 查询的优化,添加更合适的索引可以避免性能问题:执行计划使用索引并不意味着就能执行快。 参考技术A 使用全文搜索可以快速、灵活地为存储在数据库中的文本数据的基于关键字的查询创建索引。与仅适用于字符模式的LIKE谓词不同,全文查询将根据特定语言的规则对词和短语进行操作,从而针对此数据执行语言搜索。
全文索引是由SQL Server FullText Search服务来维护的,必须选启动该服务才能使用全文索引。填充全文索引有三种方式:完全填充、增量填充和更改跟踪。
在全文索引中概念与术语比较多,如全文索引、全文目录、断字符、词干分析器、标记、筛选器、填充、干扰词等。了解怎么创建全文目录,怎么创建全文索引,怎么进行全文索引的填充,怎么使用调度让全文索引自动填充。
使用CONTAINS、FREETEXT两个谓词和CONTAINSTABLE、FREETEXTTABLE两个行集值函数可以用来进行全文搜索,其中CONTAINS和FREETEXT用在WHERE子句中,CONTAINSTABLE和FREETEXTTABLE用在FROM子句中。CONTAINS搜索有简单词、派生词、前缀词、加权词和邻近词五种搜索方式。FREETEXT只有一种搜索方式,但是其将一个句子中的每个单字拆分开进行搜索。
SQL Server 2005可以对存储在image类型字段里的文件进行全文搜索。其搜索的前提是必须要有一个字段指明image类型字段里存储的文件是什么类型。当为image类型字段设置好全文索引后,可以像其他字段一样地进行全文搜索。
忽略查询中的mysql全文停用词
【中文标题】忽略查询中的mysql全文停用词【英文标题】:ignoring mysql fulltext stopwords in query 【发布时间】:2012-09-22 15:15:00 【问题描述】:我正在构建一个使用全文搜索的网站搜索。搜索本身效果很好,那不是我的问题。我将用户提供的关键字(匹配...反对...)与 AND 串在一起,以便多个词进一步缩小结果。现在,我知道某些停用词没有被索引,这对我来说很好,我真的不想将它们用作选择标准。但是,如果(由用户)在关键字集中提供了一个停用词,它会杀死所有结果(如预期的那样),即使该词实际上位于某个文本块中。
我的问题:有没有办法在查询时检查某个单词是否是停用词?我首选的解决方案是从搜索条件中排除相关的单词(我不在乎用户是否可以通过单词'neither'来缩小结果,我只是不希望 MySQL 返回一个空的结果集,因为用户提供它,即使结果中都不存在)。或者,我是否只需要清空停用词列表?非常感谢您的帮助。
编辑---- 我很抱歉,但实际上没有代码 sn-ps 可以为这个提供。该代码工作正常,实际上完全符合预期。这更像是我正在处理的一个逻辑问题。但作为一个例子,在解释的方式上:
假设有三个记录,其中包括单词(但不限于)
1:苹果、橙子、芒果、香蕉 2:葡萄、橙子、菠萝、芒果 3:土豆、芒果、甜瓜、凯拉骑士
如果用户输入的搜索词是芒果,则所有结果都正确返回。如果单词是橙色 AND 芒果,则返回结果 1 和 2(正确)。现在,假设香蕉是一个停用词(它不是……但我们假设它是),如果搜索的是橙色、芒果和香蕉,则不会返回任何结果(因为香蕉不在全文索引中)。
我正在寻找的是是否有其他人遇到过这个问题,并且有办法解决这个问题。一种:
if 'banana' NOT STOP WORD match 'banana' against `words`. (OBVIOUSLY not real code).
或者...我是否只需要删除停用词列表...
【问题讨论】:
你尝试了什么?你能给我们举个例子吗? 似乎没有一个答案能真正得到你想要的。您总是可以编写应用程序代码来手动过滤掉查询中的任何停用词(以及太短的词)。这很可能是我们要做的。 2017 和同样的问题在这里.. 似乎 mysql 全文搜索的设计真的很差,缺乏基本功能.. 【参考方案1】:您可以通过比较所有停用词来验证关键字。这是stopwords的列表 我找到了一种从全文中禁用停用词的解决方案。 你只需要找到 .cnf 文件并添加这个,
ft_stopword_file = ""
重启mysql引擎并重建索引;
希望这项工作
【讨论】:
我在想这可能是我需要走的路线。我只是真的希望其他人有一个更聪明的解决方案。感谢您的意见。【参考方案2】:如何在 MySQL 中禁用全文停用词:
在 my.ini 文本文件 (MySQL) 中:
ft_stopword_file = "" or link an empty file "empty_stopwords.txt"
ft_min_word_len = 2
// 设置您的最小长度,但请注意较短的单词 (3,2) 会显着增加查询时间,尤其是在全文索引列字段很大的情况下。
保存文件,重启服务器。
下一步应该是使用此查询修复索引:
REPAIR TABLE tbl_name QUICK.
但是,如果您的表使用 InnoDB 存储引擎,这将不起作用。您必须将其更改为 MyISAM :
ALTER TABLE t1 ENGINE = MyISAM;
所以,再一次:
1. Edit my.ini file and save
2. Restart your server (this cannot be done dynamically)
3. Change the table engine (if needed) ALTER TABLE tbl_name ENGINE = MyISAM;
4. Perform repair REPAIR TABLE tbl_name QUICK.
请注意,InnoDB 和 MyISAM 存在速度差异。一个读得更快,另一个写得更快(在互联网上阅读更多相关内容)
【讨论】:
需要注意的是,从 MySQL 5.6 开始的 InnoDB 有全文索引【参考方案3】:使用此步骤在 mysql 中禁用全文搜索的停用词
1:在mysql中打开my.ini文件
2:放在my.ini中[mysqld]行之后的两行以下(在文件中搜索[mysqld])
ft_min_word_len=1
ft_stopword_file=""
3:重启你的服务器
4:使用以下命令修复您的表
> repair table tablename;
5:现在您的搜索正在工作......
【讨论】:
【参考方案4】:尝试使用 MATCH...AGAINST...IN BOOLEAN MODE 像这个: WHERE MATCH(作者,标题) AGAINST('"origin of"' IN BOOLEAN MODE);
【讨论】:
停用词不在全文索引中,所以即使你使用布尔模式,它也不会返回任何内容【参考方案5】:设置
ft_stopword_file = ""
对我不起作用,我使用的是 INNODB 表和 MySQL 5.6(优化关联表后,停用词仍未在全文索引中编制索引)
此解决方案有效(即使您不是超级用户):
CREATE TABLE mydb.stopwordslist(value VARCHAR(20)) ENGINE = INNODB;
INSERT INTO mydb.stopwordslist(value) VALUES ('skipthisword');
适用于所有用户,但您仍需要超级用户权限:
SET GLOBAL innodb_ft_server_stopword_table = 'mydb/stopwordslist';
仅针对用户(假设是重新创建索引和更新列的用户)
SET SESSION innodb_ft_user_stopword_table = 'mydb/stopwordslist';
因为它是一个会话变量,所以当你的会话关闭时它不会持续,所以请确保你在每次会话时设置它,或者在优化或插入具有全文索引的表之前,或者当你更新由全文索引索引的列时
【讨论】:
【参考方案6】:对于 INNODB 情况,可以在创建索引时禁用 stop_words。
SET @@SESSION.innodb_ft_enable_stopword = 'OFF';
create table foo
....
fulltext (search_col)
这将导致在禁用停用词的情况下创建全文索引。您可以使用以下查询进行验证。
SET GLOBAL innodb_ft_aux_table = 'schema/foo';
select * from information_schema.innodb_ft_config;
您的结果将如下所示:
注意 use_stopword 设置为 0。
在此 mysql documentation page. 上搜索 use_stopwords
和结帐innodb_ft_enable_stopword
here
【讨论】:
以上是关于关于MSSQL 全文索引 某些词特别慢的问题的主要内容,如果未能解决你的问题,请参考以下文章