Ili*_*eda 3 sql sql-server performance full-text-search sql-server-2008-r2
我在SQL Server 2008 R2数据库中有一个表
Article (Id, art_text)
Run Code Online (Sandbox Code Playgroud)
Id是主键.art_text有一个全文索引.
我搜索包含"house"这个词的最新文章,如下所示:
SELECT TOP 100 Id, art_text
FROM Article
WHERE CONTAINS(art_text, 'house')
ORDER BY Id DESC
Run Code Online (Sandbox Code Playgroud)
这会返回正确的结果,但速度很慢(约5秒).该表有2000万行,其中350,000行包含单词house.我可以在查询计划中看到,在全文索引返回的350,000个ID的聚簇索引中执行了索引扫描.
如果有一种方法只能获得包含单词'house'的全文索引中的最新100个条目,则查询可能会快得多.有没有办法以查询更快的方式执行此操作?
简短的回答是肯定的,有一些方法可以让这个特定的查询更快乐,但是有一个2000万行的语料库,5秒也不错.您需要认真考虑以下建议是否适合您的FT搜索工作量,并权衡成本与收益.如果你盲目地实施这些,你将会度过一段美好的时光.
减少要搜索 的全文索引的大小 FT索引越小,查询越快.有几种方法可以减少FT索引的大小.前两个可能适用也可能不适用,第三个需要大量工作才能完成.
添加特定于域的干扰词干扰词 是不会为全文搜索查询增加价值的词,例如"the","and","in"等.如果有与业务相关的术语不添加任何值如果被索引,您可以从FT指数中排除它们.考虑MSDN库上的假设全文索引.诸如"Microsoft","library","include","dll"和"reference"之类的术语可能不会为搜索结果增加价值.(访问http://msdn.microsoft.com并搜索"microsoft" 是否有任何实际价值?)FT法律意见索引可能会排除诸如"被告","起诉"和"合法"等词语等.
使用iFilters删除无关数据使用 Windows iFilters进行全文搜索以从二进制文档中提取文本.这与窗口搜索功能用于搜索pdf和powerpoint文档的技术相同.这种情况特别有用的一种情况是,当您有一个可以包含HTML标记的描述列时.默认情况下,Sql Server全文搜索将索引所有内容,因此您可以将"font-family","Arial"和"href"等术语作为可搜索的术语.使用HTML iFilter可以删除标记.
在FT索引中使用iFilter的两个要求是索引列是VARBINARY,并且有一个包含文件扩展名的"type"列.这些都可以通过计算列来完成.
CREATE TABLE t (
....
description varbinary(max),
FTS_description as (CAST(description as VARBINARY(MAX)),
FTS_filetype as ( N'.html' )
)
-- Then create the fulltext index on FTS_description specifying the filetype.
Run Code Online (Sandbox Code Playgroud)表格的索引部分和拼接结果 有几种方法可以实现这一点,但总体思路是将表格分成较小的块,单独查询块并合并结果.例如,您可以创建两个索引视图,一个用于当前年份,另一个用于具有全文索引的历史年份.您返回100行的查询更改为如下所示:
DECLARE @rows int
DECLARE @ids table (id int not null primary key)
INSERT INTO @ids (id)
SELECT TOP (100) id
FROM vw_2013_FTDocuments WHERE CONTAINS (....)
ORDER BY Id DESC
SET @rows = @@rowcount
IF @rows < 100
BEGIN
DECLARE @rowsLeft int
SET @rowsLeft = 100 - @rows
INSERT INTO @ids (id) SELECT TOP (@rowsLeft) ......
--Logic to incorporate the historic data
END
SELECT ... FROM t INNER JOIN @ids .....
Run Code Online (Sandbox Code Playgroud)
这可以导致查询时间的显着减少,代价是增加搜索逻辑的复杂性.当搜索通常限于数据的子集时,该方法也适用.例如,craigslist可能有住房的FT指数,一个用于"待售",一个用于"就业".从主页进行的任何搜索都将从各个索引拼接在一起,而类别内的搜索的常见情况更有效.
您需要对与生产数量和质量相同的数据进行广泛测试.如果在未来版本的Sql server中行为发生变化,您将无权投诉.这是基于观察,而不是证据.根据自己的风险使用!!
一些全文历史记录 在Sql Server 2005中,全文搜索功能是在sqlservr.exe的外部进程中.FTS被纳入查询计划的方式是一个黑盒子.Sql server会向FTS传递一个查询,FTS会返回一个id的流.这限制了可用于Sql Server的计划,以便将FTS操作符基本上视为表扫描.
在Sql Server 2008中,FTS被集成到引擎中,从而提高了性能.它还为优化器提供了FTS查询计划的新选项.具体来说,它现在可以选择探测LOOP JOIN运算符内的FTS索引,以检查各行是否与FTS谓词匹配.(请参阅http://sqlblog.com/blogs/joe_chang/archive/2012/02/19/ query-optimizer-gone-wild-full-text.aspx可以很好地讨论这个以及出问题的方法.)
我们的最佳FTS查询计划的要求 有两个特征需要努力获得最佳的查询计划.
这两个标准消除了任何带有散列连接的计划,因为散列连接需要消耗所有一个输入来构建散列表.
对于具有循环连接的计划,有两种选择.向后扫描聚簇索引,并为每行探测到全文搜索引擎以查看该特定行是否匹配.从理论上讲,这似乎是一个很好的解决方案,因为一旦我们匹配100行,我们就完成了.我们可能需要尝试10,000个id来找到匹配的100,但这可能比阅读所有350k更好.它也可能更糟糕(参见上面链接到Joe Chang的博客)如果每个探针都很昂贵,那么我们的10k探针可能比读取所有350k行要长得多.
另一个循环连接选项是将FTS部分放在循环的外侧,并寻找聚集索引.不幸的是,FTS引擎不喜欢以相反的顺序返回结果,因此我们必须读取所有350k,然后对它们进行排序以返回前100.
包版正在使FTS引擎以相反的顺序返回行. 如果我们能够克服这个问题,那么我们可以将IO减少到仅读取匹配的最后100行.幸运的是,FTS引擎具有通过创建索引时指定的唯一索引的键按顺序返回行的趋势.(这是FTS引擎使用的内部存储的自然副作用)
通过添加一个id为负数的计算列,并在创建FT索引时在该列上指定唯一索引,那么我们真的很接近.
CREATE TABLE t (id int not null primary key, txt varchar(max), neg_id as (-id) persisted )
CREATE UNIQUE INDEX IX_t_neg_id on t (neg_id)
CREATE FULLTEXT INDEX on t ( txt ) KEY INDEX IX_t_neg_id
Run Code Online (Sandbox Code Playgroud)
现在对于我们的查询,我们将使用CONTAINSTABLE和一些LEFT-join技巧来确保FTS谓词不会最终出现在LOOP JOIN的内部.
SELECT TOP (100) t.id, t.txt
FROM CONTAINSTABLE(t, txt, 'house') ft
LEFT JOIN t on tf.[Key] = t.neg_id ORDER BY tf.[key]
Run Code Online (Sandbox Code Playgroud)
生成的计划应该是一个循环连接,它只读取FT索引中的最后100行.
小阵风可能会吹倒这个房子:
祝好运!
| 归档时间: |
|
| 查看次数: |
1860 次 |
| 最近记录: |