2010-05-01 51 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。