1. 程式人生 > >Sql Server中百萬級數據的查詢優化

Sql Server中百萬級數據的查詢優化

時也 分析 解決 普通 bject 響應時間 isnull ket b-

萬級別的數據真的算不上什麽大數據,但是這個檔的數據確實考核了普通的查詢語句的性能,不同的書寫方法有著千差萬別的性能,都在這個級別中顯現出來了,它不僅考核著你sql語句的性能,也考核著程序員的思想。

公司系統的一個查詢界面最近非常慢,界面的響應時間在6-8秒鐘時間,甚至更長。檢查發現問題出現在數據庫端,查詢比較耗時。該界面涉及到多個表中的數據,基本表有150萬數據,關聯子表的最多的一個700多萬數據,其它表數據也在幾十萬到幾百萬之間。其實按這樣的數據級別查詢響應時間應該在毫秒級內,不應該有這麽長時間。那麽接下來就該進行問題排查了。

由於這個這界面的功能主要是信息檢索,查詢比較復雜,太多的條件組合,使用存儲過程太多的局限性,因此查詢使用的是動態拼接的sql語句。查詢方式是最常用的1、獲取數據總數2、數據分頁。直接上代碼(部分條件)。

select numb=count(distinct t1.tlntcode) 
from  ZWOMMAINM0 t1 inner join ZWOMMLIBM0 t2 on t1.tlntcode=t2.tlntcode 
	join ZWOMEXPRM0 cp on t1.tlntcode=cp.tlntcode  
	join ZWOMILBSM0 i on i.tlntcode=t1.tlntcode   
	join ZWOMILBSM0 p on p.tlntcode=i.tlntcode  
	join ZWOMILBSM0 l on l.tlntcode=i.tlntcode  
where isnull(t2.deletefg,‘0‘)=‘0‘  and cp.companyn like ‘%IBM%‘  and cp.sequence=0 
	and i. mlbscode in(‘i0100‘,‘i0101‘,‘i0102‘,‘i0103‘,‘i0104‘,‘i0105‘,‘i0106‘) and i.locatype=‘10‘
	and p.mlbscode in(‘p0100‘,‘p0102‘,‘p0104‘,‘p0200‘,‘p0600‘) and p.locatype=‘10‘
	and l.mlbscode in(‘l030‘) and l.locatype=‘10‘
查看執行時間

技術分享

根據提示得知,整個查詢耗時花費在了分析和編譯為4秒,執行為0.7秒。查詢語句沒有發現什麽問題,那麽問題出現在了編譯,如果讓SQL語句執行原有的查詢計劃,那麽跳過編譯,只需0.7秒就能得到結果。那麽如何做到預編譯,或者使用現有的執行計劃?
SQL Server有一優化算法,它保存了以往執行sql語句的執行計劃,所有的執行計劃都會在sys.syscacheobjects表中存儲,如果當前sql語句在緩存表中能匹配到,那麽它講執行匹配到的執行計劃,而不再進行編譯。 那麽解決方法我們首先想到的是存儲過程(這就是我們面試或者理論中經常說的存儲過程有預編譯,平時也就是說說,不存在什麽深刻印象),是的它能實現預編譯,但是由於條件限制,查詢太過復雜,如果把沒有使用到查詢條件的表都關聯在一起反而影響到性能。排除存儲過程,我們另外想到的就是
EXEC SP_EXECUTESQL @Sql, N‘@p NVARCHAR(50)‘,@p
為什麽SP_EXECUTESQL 能復用查詢計劃而普通sql語句不能,我們從緩存表中查看就能發現問題

select bucketid,cacheobjtype,objtype,objid,sql,sqlbytes from sys.syscacheobjects where cacheobjtype=‘Compiled Plan‘

技術分享

表中sql字段就是歷史執行計劃的查詢語句,如果sql匹配成功那麽就會執行匹配的執行計劃。普通sql語句很難與之匹配,因為它不但包含了結構還包含了參數,復用率很低。而SP_EXECUTESQL 執行時只存儲結構,參數不存儲,因此復用率很高。找到了解決方法,那麽直接行動。

declare @Sql nvarchar(max),@cpny nvarchar(50)=‘IBM‘
declare @i varchar(1000)=‘i0100,i0101,i0102,i0103,i0104,i0105,i0106,i0107,i0109‘,
@p varchar(1000)=‘p0100,p0101,p0102,p0103,p0104,p0107,p0201‘,@l varchar(1000)=‘l030‘
set @Sql=‘select value into #i from f_CSplit(@i,‘‘,‘‘)
select value into #p from f_CSplit(@p,‘‘,‘‘)
select value into #l from f_CSplit(@l,‘‘,‘‘)
select numb=count(distinct t1.tlntcode)  
from  ZWOMMAINM0 t1 inner join ZWOMMLIBM0 t2 on t1.tlntcode=t2.tlntcode  
join ZWOMILBSM0 i on i.tlntcode=t1.tlntcode join ZWOMILBSM0 p on p.tlntcode=t1.tlntcode  
join ZWOMILBSM0 l on l.tlntcode=t1.tlntcode join ZWOMEXPRM0 cp on t1.tlntcode=cp.tlntcode  
where isnull(t2.deletefg,‘‘0‘‘)=‘‘0‘‘ 
and i.mlbscode in(select value from #i) and i.locatype=‘‘10‘‘ -- and i.mlbstype=‘‘20‘‘
and p.mlbscode in(select value from #p) and p.locatype=‘‘10‘‘ --and p.mlbstype=‘‘40‘‘
and l.mlbscode in(select value from #l) and l.locatype=‘‘10‘‘-- and l.mlbstype=‘‘50‘‘
and cp.companyn like ‘‘%‘‘[email protected]+‘‘%‘‘  and cp.sequence=0 ‘

EXEC SP_EXECUTESQL @Sql, N‘@cpny NVARCHAR(50),@i NVARCHAR(50),@p NVARCHAR(50),@l NVARCHAR(50)‘,
@cpny,@i,@p,@l 

技術分享

總耗時0.5秒,無論參數如何改變基本都在0.5秒波動,基本符合了我們的要求,如果想進一步優化還可以進行表分區等其他優化方案。
當我們發現查詢速度慢時,有可能是分析和編譯占用了你的太多時間,因此簡化你的查詢語句、復用執行計劃能幫你走出困境。

Sql Server中百萬級數據的查詢優化