歡迎來(lái)到性能調(diào)優(yōu)培訓(xùn)的第4個(gè)月。這個(gè)月全是關(guān)于SQL Server里的統(tǒng)計(jì)信息,還有它們?nèi)绾螏椭樵儍?yōu)化器生成足夠好的執(zhí)行計(jì)劃。統(tǒng)計(jì)信息主要是被查詢優(yōu)化器用來(lái)估計(jì)查詢返回的行數(shù)。它只是個(gè)估計(jì),沒(méi)別的。
統(tǒng)計(jì)信息概述SQL Server使用在統(tǒng)計(jì)信息對(duì)象里稱作直方圖(Histogram)的東西,它描述了對(duì)于所給列最大200步長(zhǎng)(Steps)的數(shù)據(jù)分布情況。最大的局限性之一,對(duì)于SQL Server里的統(tǒng)計(jì)信息是200步長(zhǎng)的局限性(使用過(guò)濾統(tǒng)計(jì)信息可以超過(guò)這個(gè)步長(zhǎng),這在SQL Server 2008里就引入了)。
另外的局限性是統(tǒng)計(jì)信息的自動(dòng)更新(Auto Update)機(jī)制:對(duì)于大于500行的表,如果500+20%的列值發(fā)生改變,統(tǒng)計(jì)信息才會(huì)更新。這就意味著,一旦表增長(zhǎng),你的統(tǒng)計(jì)信息的自動(dòng)更新頻率將越少(每次觸發(fā)自動(dòng)更新需要更多的記錄修改)。
假設(shè)你有100000條記錄的表,這個(gè)情況下,如果修改了20500(20%+500)的數(shù)據(jù),統(tǒng)計(jì)信息才會(huì)自動(dòng)更新。如果你有1000000條記錄的表,你需要修改200500(20%+500)的數(shù)據(jù),統(tǒng)計(jì)信息才會(huì)自動(dòng)更新。這里用到的算法是指數(shù)的,不是線性的。在SQL Server里有2371的跟蹤標(biāo)志(trace flag)也會(huì)影響這個(gè)行為。
當(dāng)你的執(zhí)行計(jì)劃里保航書(shū)簽查找時(shí),這個(gè)行為就會(huì)是巨大的問(wèn)題。正如你知道的,基于當(dāng)前的統(tǒng)計(jì)信息,如果查詢的估計(jì)行數(shù)是非常少的,查詢優(yōu)化器才會(huì)選擇書(shū)簽查找運(yùn)算符。如果你的統(tǒng)計(jì)信息過(guò)期,你的執(zhí)行計(jì)劃還是有效的話,SQL Server就會(huì)盲目重用緩存計(jì)劃,你的頁(yè)讀取就會(huì)暴漲。我們來(lái)看看這個(gè)問(wèn)題的具體例子。
失真的統(tǒng)計(jì)信息(Stale Statistics)下面的腳本會(huì)創(chuàng)建有1500條記錄的表,在column2列有平均的數(shù)據(jù)分布。另外我們?cè)赾olumn2列上定義非聚集索引。
1 CREATE TABLE Table1 2 ( 3 Column1 INT IDENTITY, 4 Column2 INT 5 ) 6 GO 7 8 -- Insert 1500 records into Table1 9 SELECT TOP 1500 IDENTITY(INT, 1, 1) AS n INTO #Nums10 FROM11 master.dbo.syscolumns sc112 13 INSERT INTO Table1 (Column2)14 SELECT n FROM #nums15 16 DROP TABLE #nums17 GO 18 19 CREATE NONCLUSTERED INDEX idx_Table1_Colum2 ON Table1(Column2)20 GO
當(dāng)你對(duì)表進(jìn)行簡(jiǎn)單的SELECT * 查詢時(shí),你會(huì)得到帶有書(shū)簽查找運(yùn)算符的執(zhí)行計(jì)劃:
1 SELECT * FROM dbo.Table1 WHERE Column2='9'
從索引查找(Non Clustered)運(yùn)算符可以看到,SQL Server估計(jì)行數(shù)是1(估計(jì)行數(shù)(Estimated Number of Rows)屬性),實(shí)際上SQL Server也處理1條記錄(實(shí)際行數(shù)(Actual Number of Rows)屬性)。這就是說(shuō),我們這里用到的統(tǒng)計(jì)信息是準(zhǔn)確的,查詢本身產(chǎn)生3個(gè)邏輯讀。
我們現(xiàn)在的表有1500條記錄,因此當(dāng)20% + 500條記錄發(fā)生改變時(shí),SQL Server會(huì)自動(dòng)更新非聚集索引的統(tǒng)計(jì)信息。算一下,我們需要修改800條數(shù)據(jù)(1500 * 20% + 500)。
接下來(lái)我們對(duì)表做如下處理:我們對(duì)SQL Server做一點(diǎn)動(dòng)作,只插入799條新記錄。但799條記錄的第2列值都是2。這就是說(shuō)我們完全改變第2列的平均數(shù)據(jù)分布。統(tǒng)計(jì)信息會(huì)認(rèn)為只有1條第2列值為2的記錄返回,但實(shí)際上卻有800條記錄返回(1條已存在的,799條新插入的):
1 SELECT TOP 799 IDENTITY(INT, 1, 1) AS n INTO #Nums2 FROM3 master.dbo.syscolumns sc14 5 INSERT INTO Table1 (Column2)6 SELECT 2 FROM #nums7 8 DROP TABLE #nums9 GO
現(xiàn)在我們來(lái)執(zhí)行下列查詢語(yǔ)句,找第2列值為2的記錄,并打開(kāi)執(zhí)行計(jì)劃顯示和IO統(tǒng)計(jì)。
1 SET STATISTICS IO ON2 SELECT * FROM dbo.Table1 WHERE Column2 ='2'
SQL Server重用了有書(shū)簽查找的執(zhí)行計(jì)劃。這就是說(shuō)執(zhí)行計(jì)劃里的書(shū)簽查找執(zhí)行了1500次——一次性對(duì)所有記錄!這會(huì)耗費(fèi)大量的邏輯讀——SQL Server這里報(bào)告了806個(gè)頁(yè)讀取。
從圖中可以看到,實(shí)際行數(shù)(Actual Number of Rows)現(xiàn)在已經(jīng)遠(yuǎn)遠(yuǎn)超過(guò)了估計(jì)行數(shù)(Estimated Number of Rows)。
SQL Server里失真的統(tǒng)計(jì)信息就會(huì)帶來(lái)這樣的問(wèn)題。
小結(jié)今天的性能調(diào)優(yōu)培訓(xùn)我給你簡(jiǎn)單介紹了SQL Server里的統(tǒng)計(jì)信息。如你所見(jiàn),失真的統(tǒng)計(jì)信息,對(duì)于緩存的,重用的執(zhí)行計(jì)劃會(huì)帶來(lái)嚴(yán)重的性能問(wèn)題。
我希望現(xiàn)在你已經(jīng)能很好的理解SQL Server里的統(tǒng)計(jì)信息,當(dāng)它們過(guò)期是,會(huì)給你的執(zhí)行計(jì)劃帶來(lái)副作用。下周我會(huì)進(jìn)一步討論統(tǒng)計(jì)信息,還有在SQL Server內(nèi)部它們是怎樣的。請(qǐng)繼續(xù)關(guān)注。
新聞熱點(diǎn)
疑難解答
圖片精選