2010-05-01 109 views
12

我正在使用sql 2008全文搜索,而且我的性能有严重问题,这取决于我如何使用Contains或ContainsTable。用于JOIN的Sql serve全文搜索与Containstable非常缓慢!

下面是示例:(表1有大约5000条记录,并且table1上有一个覆盖索引,它包含where子句中的所有字段。我尝试简化语句,所以如果有语法问题,请原谅我。)

方案1:

select * from table1 as t1 
where t1.field1=90 
and t1.field2='something' 
and Exists(select top 1 * from containstable(table1,*, 'something') as t2 
where t2.[key]=t1.id) 

的结果:10秒(非常慢的)

方案2:

select * from table1 as t1 
join containstable(table1,*, 'something') as t2 on t2.[key] = t1.id 
where t1.field1=90 
and t1.field2='something' 

结果:10秒(非常慢)

方案3:

Declare @tbl Table(id uniqueidentifier primary key) 
insert into @tbl select {key] from containstable(table1,*, 'something') 

select * from table1 as t1 
where t1.field1=90 
and t1.field2='something' 
and Exists(select id from @tbl as tbl where id=req1.id) 

结果:第二(超快速)

底线的分数,这似乎如果我在任何类型的连接中使用Containstable或者在其他条件的select语句的where子句条件下,则性能非常糟糕。另外,如果您查看剖析器,则从数据库读取的数量将进入屋顶。但是,如果我首先进行全文搜索并将结果放入一个表格变量中并使用该变量,则所有内容都变得非常快。读取次数也低得多。看起来在“糟糕”的情况下,它以某种方式卡在循环中,导致它从数据库中读取很多次,但当然我不明白为什么。

现在的问题首先是为什么发生这种情况?问题二是如何扩展表变量?如果结果是成千上万的记录呢?它仍然会很快。

任何想法? 谢谢

+0

似乎与这张海报有关的问题http://stackoverflow.com/questions/2746303/freetext-query-is-slow-includes-top-and-order-by/2749322#2749322。查看执行计划时,自由文本查询中的估计行数和实际行数是多少? – 2010-05-01 17:37:25

+0

哦,并且在回答关于表变量的查询时,如果你打算将它们加入到其他表中,它们肯定是不可缩放的。查询优化器总是假设它们只返回1行,如果它们有数万条记录,则可能导致一些非常优化的计划。临时表确实为他们创建了统计数据。 – 2010-05-01 18:03:10

回答

3

我在这里会猜测你的问题与我链接到的其他线程相同。您是否发现多词搜索术语出现问题?

如果是这样,我从该线索的答案将适用。

http://technet.microsoft.com/en-us/library/cc721269.aspx#_Toc202506240

最重要的事情是正确的 连接类型被钦点为 全文查询。基数 对FulltextMatch STVF 的估算对于正确的计划非常重要。 因此,首先要检查的是FulltextMatch cardinality估计值 。 这是在全文搜索 字符串的索引中的匹配数 的估计值。例如,在 图3中的查询中,这应该接近包含 术语“单词”的 数量的文档。在大多数情况下,它应该是非常准确的 ,但如果估计 关闭了很长时间,您可能会生成错误的计划 。对于 单一方面的估计通常是很不错的, 但估计多个方面,如 短语或和查询更加复杂 ,因为它是不可能知道什么 方面的索引 交集将根据频率索引中的 条款。如果基数 估计是好的,则可能是由查询 优化器成本模型引起的错误计划 。 解决计划问题的唯一方法是使用查询 提示强制某种加入 或OPTIMIZE FOR。

因此,它根本无法从它存储的信息中知道它存储的2个搜索术语是否可能相当独立或共同共同发现。也许你应该有两个独立的程序,一个用于单词查询,你可以让优化器完成它的工作,另一个用于多词搜索术语,你可以强制执行一个“足够好”的计划(sys.dm_fts_index_keywords可能有助于如果你想做一个自己对基数的粗略估计)。

如果您使用单个单词查询时遇到问题,则可以应用链接文章中的这段内容。

在SQL Server 2008全文搜索中,我们有能力更改基于所用搜索术语的基数估计生成的计划 。如果查询计划是固定的(因为它位于存储过程中的参数化查询中),则此步骤不会发生 。因此,即使此计划对于给定搜索词不理想,编译计划也会始终为此查询提供服务。

因此,您可能需要使用RECOMPILE选项。

+0

感谢您的精心回应。但是,我的问题不是用多个词。事实上,无论是单词还是多个单词,对全文的搜索总是非常快速。我的问题是,当全文搜索与where子句中的其他条件结合使用时,性能会急剧下降。 由于我要走出这个评论的大小的允许限制,请参阅我的答案的其余部分的下一个评论... – Bob 2010-05-04 17:42:04

+0

@Bob仍然很奇怪,当你从你的表变量加入,它基本上执行加入ID到关键的相同的事情,它工作正常。它如何进行连接 - 它选择了不同的索引 - 还是不同的连接策略?另外,当你看执行计划时,估计的和实际的行对所有部分都是合理正确的? – 2010-05-04 19:06:23

12

我花了相当一段时间在这个问题上,并根据运行许多情况下,这是我想通了:

,如果您有包含或CONTAINSTABLE在查询中的任何地方,那就是最先被执行的部分,相当独立。这意味着,即使其他条件限制您的搜索只有一条记录,既不包含也不包含关于这一点的稳定关心。所以这就像一个平行执行。

现在,由于全文搜索仅返回一个Key字段,因此它立即查找该Key作为为查询选择的其他索引的第一个字段。因此,对于上面的示例,它使用[key],field1,field2查找索引。问题在于它会根据where子句中的字段为其余查询选择一个索引。所以对于上面的例子,它选择了我有的那个像field1,field2,Id那样的覆盖索引。 (表格的ID与从全文搜索返回的[Key]相同)。所以总结是:

  1. 基于该查询
  2. 的条款它试图合并这两个执行CONTAINSTABLE
  3. 执行查询的其余部分,选择一个指标。因此,如果它为查询的其余部分选择的索引以[key]字段开头,那就没有问题。但是,如果索引没有[key]字段作为第一个键,它将开始执行循环。它甚至不进行表扫描,否则通过5000条记录不会那么慢。循环的方式是运行FTS结果总数乘以查询其余部分结果总数的循环。因此,如果FTS返回2000条记录,并且查询的其余部分返回3000,则循环2000 * 3000 = 6,000,000。我不懂为什么。

因此,在我的情况下,它会进行全文搜索,然后它会完成查询的其余部分,但是会根据field1,field2,id(这是错误的)导致它拧紧。如果我将覆盖索引更改为Id,field1,field2,则一切都会非常快。

我期待的是FTS返回一堆[key],其余的查询返回一堆[Id],然后Id应该与[key]匹配。

当然,我试图在这里简化我的查询,但实际的查询要复杂得多,我不能只是改变索引。我也有一些场景,全文文本是空白的,在这些场景中,我甚至不想加入到containstable中。 在这些情况下,将我的覆盖索引更改为以id字段作为第一个字段,将会产生灾难。

无论如何,现在我选择了临时表解决方案,因为它为我工作。我也将结果限制为几千个,这会在记录数量过高时帮助解决表变量的潜在性能问题。

感谢

7

通常情况下它的工作原理非常快:

select t1.*, t2.Rank 
    from containstable(table1, field2, 'something') as t2 
     join table1 as t1 ON t1.id = t2.Key AND t1.field1=90 
    order by t2.Rank desc 

有,你把你的搜索条件有很大的不同:在JOIN或WHERE。