SQL优化技巧--远程连接对象引起的CTE性能问题
背景
最近SSIS的開發(fā)過程中遇到幾個問題。其中使用CTE時,遇到一個遠程連接對象,結(jié)果導(dǎo)致嚴重的性能問題,為了應(yīng)急我就修改了代碼。
之前我寫了一篇介紹CTE的隨筆包含了CTE的用法等:
???? http://wudataoge.blog.163.com/blog/static/80073886200961652022389/
問題
在一個數(shù)據(jù)查詢中遇到一個遠程連接對象,然后使用了CTE,然后本地查詢與遠程對象的CTE進行了left join 。下面就是執(zhí)行計劃:
首先我們發(fā)現(xiàn),最后一個操作符顯示遠程查詢占了99%。
注意:
首先,遠程查詢使用的是CTE的表達式,我對CTE的理解有以下幾點:
1.一次性視圖(ADHoc View)。即必須后面跟著相應(yīng)的select、insert、update等,只能用一次。
2.CTE表達式也是在內(nèi)存中創(chuàng)建了一個表并對其操作。
3.with as 部分僅僅是一個封裝定義的對象,并沒有真的查詢。
3.除非本身具有索引否則CTE中是沒有索引和約束的。
4.沒有專門的統(tǒng)計信息,這點與表變量很像。有可能會有錯誤的統(tǒng)計信息。
?
其次,連接操作符使用的是循環(huán)嵌套的操作符。這樣就幾何翻倍了查詢的時間。
這里需要說一下NestedLoops:
本質(zhì)上講,“Nested Loops”操作符就是:為每一個記錄的外部輸入找到內(nèi)部輸入的匹配行。
技術(shù)上講,這意味著外表聚集索引被掃描獲取外部輸入相關(guān)的記錄,然后內(nèi)表聚集索引查找每一個匹配外表索引的記錄。
以上兩個說法都表明了這種方式導(dǎo)致的性能問題。因為每一次循環(huán)都要訪問一次鏈接服務(wù)器。當(dāng)數(shù)據(jù)很大的時候極大地增加了查詢時間。我這邊70000+的數(shù)據(jù)執(zhí)行了半小時。
解決:
既然了解了問題的情況,那我就著手解決問題。主要是兩分解成兩個步驟:
1.將遠程鏈接服務(wù)器的查詢結(jié)果插入臨時表。
2.本地數(shù)據(jù)與臨時表做left join。
對應(yīng)的執(zhí)行計劃如下:
可以看到整個性能得到了極大的提高。修改完成后執(zhí)行時間縮減到20秒以內(nèi)。效率還是驚人的。
可以對比一下表變量與cte表倒是不同的特點:
- tempdb中實際存在的表
- 能索引
- 有約束
- 在當(dāng)前連接中存在,退出后自動刪除。
- 有由引擎生成的數(shù)據(jù)統(tǒng)計。
通過兩個方式的不同點可知幾種情況不應(yīng)當(dāng)使用CTE:
1.結(jié)果集較大時不應(yīng)使用。
2.查詢時間較長的不要使用,比如跨服務(wù)器查詢。
3.需要大的表連接的,比如行很多的各種join。尤其沒有索引。
4.多次查詢數(shù)據(jù)。
5.需要優(yōu)化相關(guān)子查詢。
這些時候使用臨時表甚至表變量將會帶來性能的提升。具體我就不在這里細說了有興趣可以一起討論下。
一些網(wǎng)上的錯誤:
1.materialize 提示 可以強制將WITH AS短語里的數(shù)據(jù)放入一個全局臨時表里。sql server中根本沒有這個提示。據(jù)說2014以后可能會有?
2.CTE 性能要差,根據(jù)實際情況出發(fā),據(jù)我所知在絕大多數(shù)情況下,CTE的性能要好。尤其是對比游標(迭代)和內(nèi)置函數(shù)的情況下,都會大大提高性能。
3.CTE使用了tempdb,沒有僅僅使用了內(nèi)存。
總結(jié):
通過解決實際問題,讓我了解了CTE的運行機制。可以理解為一種一次性的視圖。當(dāng)然我們這里需要著重說明,CTE本身在性能優(yōu)化上還是有很大作用的,尤其對于遞歸查詢和內(nèi)置函數(shù)的使用時都極大的較少了IO。
我猜想CTE內(nèi)部原理應(yīng)該與游標相似,但是極大的簡化了性能,也許是優(yōu)化器的功勞。最后由于僅僅使用了內(nèi)存中這樣也大大減少了連接瓶頸。
這部分很多是我的個人觀點,希望各位大神幫忙指摘一下。
總結(jié)
以上是生活随笔為你收集整理的SQL优化技巧--远程连接对象引起的CTE性能问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 腾讯会议怎么看回放视频
- 下一篇: 使用SQL Server 发送邮件