對於索引假脫機的一點理解


對於索引假脫機的一點理解

在SQLSERVER執行計划里不知道大家有沒有看過“索引假脫機”這個運算符

在QQ群里綜合了各位大俠的解釋:假脫機 有索引假脫機 和 表假脫機兩種

先來運行一下下面的SQL代碼:

 1 USE [tempdb]
 2 GO
 3 create table   #tb(aa   int,bb   char(1)) 
 4 GO  
 5   insert   #tb   values(1,'A')   
 6   insert   #tb   values(1,'B')   
 7   insert   #tb   values(1,'C')   
 8   insert   #tb   values(1,'D')   
 9 
10   insert   #tb   values(2,'E')   
11   insert   #tb   values(2,'F')   
12   insert   #tb   values(2,'G')   
13   insert   #tb   values(2,'H')   
14 
15   insert   #tb   values(3,'I')   
16   insert   #tb   values(3,'J')   
17   insert   #tb   values(3,'K')   
18   insert   #tb   values(3,'L') 
 1  --SQL1
 2  SELECT * FROM #tb a
 3  WHERE  bb IN 
 4      (
 5      SELECT TOP 1 bb FROM #tb 
 6      WHERE aa=a.aa
 7      ORDER BY NEWID()
 8      )
 9  
10   --SQL2 
11   SELECT * FROM #tb a
12   WHERE  bb = 
13      (
14      SELECT TOP 1 bb FROM #tb 
15      WHERE aa=a.aa
16      ORDER BY NEWID()
17      ) 
18  
19   --drop table tb

你會發現SQL1的執行計划和SQL2的執行計划很不一樣
SQL1的執行計划

SQL2的執行計划

對於執行計划的解釋:

1 據樓主的語句, 第一個使用的是IN, 所以IN 里面的, 被查詢優化器判定為 subquery (子查詢)
2 
3 而第二個使用的是 =, 所以查詢優化器判定 = 后面的是一個表達式
4 
5 因此在判定查詢方法的時候, 產生了差異, 對於查詢, 查詢優化器認為具有不確定性, 所以每條記錄都要去執行一次子查詢; 而對於表達式, 查詢優化器認為它具有確定性, 所以對於每個 aa, 計算一次就行了
6 
7 如果把樓主的第一個查詢中的 IN, 也改成表達式, 則可以看到會使用與查詢2一樣的執行計划, 結果也也查詢2一樣, 有固定的記錄數


但是還是有些雲里霧里,然后聽了QQ群里面的某位大俠的解釋

1 XXXX(17478043) 9:28:19 
2 索引假脫機是 系統在查詢的時候表和數據放入tempdb里然后臨時創建一個索引
3 ,表假脫機是為了避免 重復更新某些列,從而提高性能,
4 估計是把整張表放入tempdb,因為那張表更新頻繁,
5 所以SQL決定把整個表放入tempdb做下一步的排序或者其他操作,
6 不受其他的更新插入操作影響,這是我的個人理解

而出現索引假脫機的時候,那么表明需要做一些優化,例如加索引

1 XXXXXX(17478043) 9:29:18 
2 出現索引假脫機說明你缺少某些很重要的索引
3 創建它就可以了
4 XXXXXXX 9:30:08 
5 我看你昨天那個有個 排序 運算符,一般這樣的運算需要使用排序或者索引

回到上面的例子,為什麽SQL1沒有索引假脫機呢?

因為SQL1里使用in具有不確定性,而SQL2使用=具有確定性,然后SQL認為每次運行都需要排序干脆加一個索引算了

所以SQL2才有了“索引假脫機”這個運算符

在MSDN上找到了tempdb的其中一個用途:

1 tempdb 系統數據庫是一個全局資源,可供連接到 SQL Server 實例的所有用戶使用,並可用於保存下列各項: 
2 
3 
4 •SQL Server 2005 數據庫引擎創建的內部對象,例如,用於存儲假脫機或排序的中間結果的工作表。

剛好QQ群里的某君也遇到“索引假脫機”

 

 

大家看了這個執行計划之后都解釋了出現索引假脫機的原因:

聊天記錄:

 1 數據庫認為幫你建一個索引再查找還快過直接掃描
 2 XXXXX<huangzj1985@qq.com> 16:06:03 
 3 XXXXXXX,那如何優化?
 4 XXXXXXXXX17478043) 16:06:37 
 5 建一個索引唄。。
 6 XXXXXXXXX(17478043) 16:07:07 
 7 建了消除這個索引假脫機運算符了
 8 
 9 XXXXXXXXXXXX(17478043) 16:07:51 
10 一般情況下 需要用到聚合的列都應該有對應的索引
11 
12 XXXXXXXXX(17478043) 16:08:15 
13 因為聚合的第一步就是排序
14 缺少索引就會容易出現哈希運算

后來大家都給出了解決方案建議之后確實消除了這個“索引假脫機”了

 1 seek 謂詞 就是 索引列,這里應該是組合索引,
 2 輸出放到 include列
 3 當然,組合順序得考慮數據的分布情況,還有查詢的語句,為了DML的性能考慮 ,可以把一些 選擇性差的列放到 include 列
 4 包含性索引列
 5 
 6 
 7 XXXXXX 剛才那個  我加了索引和include 包含性索引列   
 8 確實不會有 索引假脫機了 
 9  
10 XXXXXXXX<huangzj1985@qq.com> 9:50:39 
11 速度方面呢?
12 XXXXXXXXXX(771021218) 9:50:44 
13 索引假脫機  變成了 索引查找  消耗55%

現在加了索引性能好多了

所以以后大家看到“索引假脫機”不要以為SQLSERVER的索引沒有起作用了,脫機了~


總結

其實關於脫機還有很多情況的,包括:lazy spool、Eagar spool、table spool、non clustered index spool...

在tempdb數據庫中緩存用來處理一致性和避免hit

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM