對於索引假脫機的一點理解
在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