濮阳杆衣贸易有限公司

主頁(yè) > 知識(shí)庫(kù) > 淺析SQL Server中的執(zhí)行計(jì)劃緩存(上)

淺析SQL Server中的執(zhí)行計(jì)劃緩存(上)

熱門(mén)標(biāo)簽:疫情時(shí)期電話機(jī)器人 樂(lè)昌電話機(jī)器人 地圖標(biāo)注可以編輯地名嗎 南京怎么申請(qǐng)400這種電話 南通智能外呼系統(tǒng)怎么樣 臺(tái)灣外呼系統(tǒng)軟件 真3地圖標(biāo)注 濮陽(yáng)清豐400開(kāi)頭的電話申請(qǐng) 地圖標(biāo)注跑線下市場(chǎng)

簡(jiǎn)介

我們平時(shí)所寫(xiě)的SQL語(yǔ)句本質(zhì)只是獲取數(shù)據(jù)的邏輯,而不是獲取數(shù)據(jù)的物理路徑。當(dāng)我們寫(xiě)的SQL語(yǔ)句傳到SQL Server的時(shí)候,查詢(xún)分析器會(huì)將語(yǔ)句依次進(jìn)行解析(Parse)、綁定(Bind)、查詢(xún)優(yōu)化(Optimization,有時(shí)候也被稱(chēng)為簡(jiǎn)化)、執(zhí)行(Execution)。除去執(zhí)行步驟外,前三個(gè)步驟之后就生成了執(zhí)行計(jì)劃,也就是SQL Server按照該計(jì)劃獲取物理數(shù)據(jù)方式,最后執(zhí)行步驟按照?qǐng)?zhí)行計(jì)劃執(zhí)行查詢(xún)從而獲得結(jié)果。但查詢(xún)優(yōu)化器不是本篇的重點(diǎn),本篇文章主要講述查詢(xún)優(yōu)化器在生成執(zhí)行計(jì)劃之后,緩存執(zhí)行計(jì)劃的相關(guān)機(jī)制以及常見(jiàn)問(wèn)題。

為什么需要執(zhí)行計(jì)劃緩存

從簡(jiǎn)介中我們知道,生成執(zhí)行計(jì)劃的過(guò)程步驟所占的比例眾多,會(huì)消耗掉各CPU和內(nèi)存資源。而實(shí)際上,查詢(xún)優(yōu)化器生成執(zhí)行計(jì)劃要做更多的工作,大概分為3部分:

首先,根據(jù)傳入的查詢(xún)語(yǔ)句文本,解析表名稱(chēng)、存儲(chǔ)過(guò)程名稱(chēng)、視圖名稱(chēng)等。然后基于邏輯數(shù)據(jù)操作生成代表查詢(xún)文本的樹(shù)。

第二步是優(yōu)化和簡(jiǎn)化,比如說(shuō)將子查詢(xún)轉(zhuǎn)換成對(duì)等的連接、優(yōu)先應(yīng)用過(guò)濾條件、刪除不必要的連接(比如說(shuō)有索引,可能不需要引用原表)等。

第三步根據(jù)數(shù)據(jù)庫(kù)中的統(tǒng)計(jì)信息,進(jìn)行基于成本(Cost-based)的評(píng)估。

上面三個(gè)步驟完成之后,才會(huì)生成多個(gè)候選執(zhí)行計(jì)劃。雖然我們的SQL語(yǔ)句邏輯上只有一個(gè),但是符合這個(gè)邏輯順序的物理獲取數(shù)據(jù)的順序卻可以有多條,打個(gè)比方,你希望從北京到上海,即可以做高鐵,也可以做飛機(jī),但從北京到上海這個(gè)描述是邏輯描述,具體怎么實(shí)現(xiàn)路徑有多條。那讓我們?cè)倏匆粋€(gè)SQL Server中的舉例,比如代碼清單1中的查詢(xún)。

SELECT * 
FROM A INNER JOIN B ON a.a=b.b
INNER JOIN C ON c.c=a.a

代碼清單1.

對(duì)于該查詢(xún)來(lái)說(shuō),無(wú)論A先Inner join B還是B先Inner Join C,結(jié)果都是一樣的,因此可以生成多個(gè)執(zhí)行計(jì)劃,但一個(gè)基本原則是SQL Server不一定會(huì)選擇最好的執(zhí)行計(jì)劃,而是選擇足夠好的計(jì)劃,這是由于評(píng)估所有的執(zhí)行計(jì)劃的成本所消耗的成本不應(yīng)該過(guò)大。最終,SQL Server會(huì)根據(jù)數(shù)據(jù)的基數(shù)和每一步所消耗的CPU和IO的成本來(lái)評(píng)估執(zhí)行計(jì)劃的成本,所以執(zhí)行計(jì)劃的選擇重度依賴(lài)于統(tǒng)計(jì)信息,關(guān)于統(tǒng)計(jì)信息的相關(guān)內(nèi)容,我就不細(xì)說(shuō)了。

對(duì)于前面查詢(xún)分析器生成執(zhí)行計(jì)劃的過(guò)程不難看出,該步驟消耗的資源成本也是驚人的。因此當(dāng)同樣的查詢(xún)執(zhí)行一次以后,將其緩存起來(lái)將會(huì)大大減少執(zhí)行計(jì)劃的編譯,從而提高效率,這就是執(zhí)行計(jì)劃緩存存在的初衷。

執(zhí)行計(jì)劃所緩存的對(duì)象

執(zhí)行計(jì)劃所緩存的對(duì)象分為4類(lèi),分別是:

編譯后的計(jì)劃:編譯的執(zhí)行計(jì)劃和執(zhí)行計(jì)劃的關(guān)系就和MSIL和C#的關(guān)系一樣。

執(zhí)行上下文:在執(zhí)行編譯的計(jì)劃時(shí),會(huì)有上下文環(huán)境。因?yàn)榫幾g的計(jì)劃可以被多個(gè)用戶共享,但查詢(xún)需要存儲(chǔ)SET信息以及本地變量的值等,因此上下文環(huán)境需要對(duì)應(yīng)執(zhí)行計(jì)劃進(jìn)行關(guān)聯(lián)。執(zhí)行上下文也被稱(chēng)為Executable Plan。

游標(biāo):存儲(chǔ)的游標(biāo)狀態(tài)類(lèi)似于執(zhí)行上下文和編譯的計(jì)劃的關(guān)系。游標(biāo)本身只能被某個(gè)連接使用,但游標(biāo)關(guān)聯(lián)的執(zhí)行計(jì)劃可以被多個(gè)用戶共享。

代數(shù)樹(shù):代數(shù)樹(shù)(也被稱(chēng)為解析樹(shù))代表著查詢(xún)文本。正如我們之前所說(shuō),查詢(xún)分析器不會(huì)直接引用查詢(xún)文本,而是代數(shù)樹(shù)。這里或許你會(huì)有疑問(wèn),代數(shù)樹(shù)用于生成執(zhí)行計(jì)劃,這里還緩存代數(shù)樹(shù)干毛???這是因?yàn)橐晥D、Default、約束可能會(huì)被不同查詢(xún)重復(fù)使用,將這些對(duì)象的代數(shù)樹(shù)緩存起來(lái)省去了解析的過(guò)程。

比如說(shuō)我們可以通過(guò)dm_exec_cached_plans這個(gè)DMV找到被緩存的執(zhí)行計(jì)劃,如圖1所示。

圖1.被緩存的執(zhí)行計(jì)劃

那究竟這幾類(lèi)對(duì)象緩存所占用的內(nèi)存相關(guān)信息該怎么看呢?我們可以通過(guò)dm_os_memory_cache_counters這個(gè)DMV看到,上述幾類(lèi)被緩存的對(duì)象如圖2所示。

圖2.在內(nèi)存中這幾類(lèi)對(duì)象緩存所占用的內(nèi)存

另外,執(zhí)行計(jì)劃緩存是一種緩存。而緩存中的對(duì)象會(huì)根據(jù)算法被替換掉。對(duì)于執(zhí)行計(jì)劃緩存來(lái)說(shuō),被替換的算法主要是基于內(nèi)存壓力。而內(nèi)存壓力會(huì)被分為兩種,既內(nèi)部壓力和外部壓力。外部壓力是由于Buffer Pool的可用空間降到某一臨界值(該臨界值會(huì)根據(jù)物理內(nèi)存的大小而不同,如果設(shè)置了最大內(nèi)存則根據(jù)最大內(nèi)存來(lái))。內(nèi)部壓力是由于執(zhí)行計(jì)劃緩存中的對(duì)象超過(guò)某一個(gè)閾值,比如說(shuō)32位的SQL Server該閾值為40000,而64位中該值被提升到了160000。

這里重點(diǎn)說(shuō)一下,緩存的標(biāo)識(shí)符是查詢(xún)語(yǔ)句本身,因此select * from SchemaName.TableName和Select * from TableName雖然效果一致,但需要緩存兩份執(zhí)行計(jì)劃,所以一個(gè)Best Practice是在引用表名稱(chēng)和以及其他對(duì)象的名稱(chēng)時(shí),請(qǐng)帶上架構(gòu)名稱(chēng)。
基于被緩存的執(zhí)行計(jì)劃對(duì)語(yǔ)句進(jìn)行調(diào)優(yōu)

被緩存的執(zhí)行計(jì)劃所存儲(chǔ)的內(nèi)容非常豐富,不僅僅包括被緩存的執(zhí)行計(jì)劃、語(yǔ)句,還包括被緩存執(zhí)行計(jì)劃的統(tǒng)計(jì)信息,比如說(shuō)CPU的使用、等待時(shí)間等。但這里值得注意的是,這里的統(tǒng)計(jì)只算執(zhí)行時(shí)間,而不算編譯時(shí)間。比如說(shuō)我們可以利用代碼清單2中的代碼根據(jù)被緩存的執(zhí)行計(jì)劃找到數(shù)據(jù)庫(kù)中耗時(shí)最長(zhǎng)的20個(gè)查詢(xún)語(yǔ)句。

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT TOP 20 
 CAST(qs.total_elapsed_time / 1000000.0 AS DECIMAL(28, 2)) 
                   AS [Total Duration (s)] 
 , CAST(qs.total_worker_time * 100.0 / qs.total_elapsed_time 
                AS DECIMAL(28, 2)) AS [% CPU] 
 , CAST((qs.total_elapsed_time - qs.total_worker_time)* 100.0 / 
    qs.total_elapsed_time AS DECIMAL(28, 2)) AS [% Waiting] 
 , qs.execution_count 
 , CAST(qs.total_elapsed_time / 1000000.0 / qs.execution_count 
        AS DECIMAL(28, 2)) AS [Average Duration (s)] 
 , SUBSTRING (qt.text,(qs.statement_start_offset/2) + 1,   
  ((CASE WHEN qs.statement_end_offset = -1 
   THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2 
   ELSE qs.statement_end_offset 
   END - qs.statement_start_offset)/2) + 1) AS [Individual Query 
 , qt.text AS [Parent Query] 
 , DB_NAME(qt.dbid) AS DatabaseName 
 , qp.query_plan 
FROM sys.dm_exec_query_stats qs 
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as qt 
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp 
WHERE qs.total_elapsed_time > 0 
ORDER BY qs.total_elapsed_time DESC

代碼清單2.通過(guò)執(zhí)行計(jì)劃緩存找到數(shù)據(jù)庫(kù)總耗時(shí)最長(zhǎng)的20個(gè)查詢(xún)語(yǔ)句

上面的語(yǔ)句您可以修改Order By來(lái)根據(jù)不同的條件找到你希望找到的語(yǔ)句,這里就不再細(xì)說(shuō)了。

相比較于無(wú)論是服務(wù)端Trace還是客戶端的Profiler,該方法有一定優(yōu)勢(shì),如果通過(guò)捕捉Trace再分析的話,不僅費(fèi)時(shí)費(fèi)力,還會(huì)給服務(wù)器帶來(lái)額外的開(kāi)銷(xiāo),通過(guò)該方法找到耗時(shí)的查詢(xún)語(yǔ)句就會(huì)簡(jiǎn)單很多。但是該統(tǒng)計(jì)僅僅基于上次實(shí)例重啟或者沒(méi)有運(yùn)行DBCC FreeProcCache之后。但該方法也有一些弊端,比如說(shuō):

類(lèi)似索引重建、更新統(tǒng)計(jì)信息這類(lèi)語(yǔ)句是不緩存的,而這些語(yǔ)句成本會(huì)非常高。
緩存可能隨時(shí)會(huì)被替換掉,因此該方法無(wú)法看到不再緩存中的語(yǔ)句。
該統(tǒng)計(jì)信息只能看到執(zhí)行成本,無(wú)法看到編譯成本。
沒(méi)有參數(shù)化的緩存可能同一個(gè)語(yǔ)句呈現(xiàn)不同的執(zhí)行計(jì)劃,因此出現(xiàn)不同的緩存,在這種情況下統(tǒng)計(jì)信息無(wú)法累計(jì),可能造成不是很準(zhǔn)確。

執(zhí)行計(jì)劃緩存和查詢(xún)優(yōu)化器的矛盾

還記得我們之前所說(shuō)的嗎,執(zhí)行計(jì)劃的編譯和選擇分為三步,其中前兩步僅僅根據(jù)查詢(xún)語(yǔ)句和表等對(duì)象的metadata,在執(zhí)行計(jì)劃選擇的階段要重度依賴(lài)于統(tǒng)計(jì)信息,因此同一個(gè)語(yǔ)句僅僅是參數(shù)的不同,查詢(xún)優(yōu)化器就會(huì)產(chǎn)生不同的執(zhí)行計(jì)劃,比如說(shuō)我們來(lái)看一個(gè)簡(jiǎn)單的例子,如圖3所示。

圖3.僅僅是由于不同的參數(shù),查詢(xún)優(yōu)化器選擇不同的執(zhí)行計(jì)劃

大家可能會(huì)覺(jué)得,這不是挺好的嘛,根據(jù)參數(shù)產(chǎn)生不同的執(zhí)行計(jì)劃。那讓我們?cè)倏紤]一個(gè)問(wèn)題,如果將上面的查詢(xún)放到一個(gè)存儲(chǔ)過(guò)程中,參數(shù)不能被直接嗅探到,當(dāng)?shù)谝粋€(gè)執(zhí)行計(jì)劃被緩存后,第二次執(zhí)行會(huì)復(fù)用第一次的執(zhí)行計(jì)劃!雖然免去了編譯時(shí)間,但不好的執(zhí)行計(jì)劃所消耗的成本會(huì)更高!讓我們來(lái)看這個(gè)例子,如圖4所示。

圖4.不同的參數(shù),卻是完全一樣的執(zhí)行計(jì)劃! 

再讓我們看同一個(gè)例子,把執(zhí)行順序顛倒后,如圖5所示。

圖5.執(zhí)行計(jì)劃完全變了

我們看到,第二次執(zhí)行的語(yǔ)句,完全復(fù)用了第一次的執(zhí)行計(jì)劃。那總會(huì)有一個(gè)查詢(xún)犧牲。比如說(shuō)當(dāng)參數(shù)為4時(shí)會(huì)有5000多條,此時(shí)索引掃描應(yīng)該最高效,但圖4卻復(fù)用了上一個(gè)執(zhí)行計(jì)劃,使用了5000多次查找?。?!這無(wú)疑是低效率的。而且這種情況出現(xiàn)會(huì)非常讓DBA迷茫,因?yàn)樵诰彺嬷械膱?zhí)行計(jì)劃不可控,緩存中的對(duì)象隨時(shí)可能被刪除,誰(shuí)先執(zhí)行誰(shuí)后執(zhí)行產(chǎn)生的性能問(wèn)題往往也讓DBA頭疼。

由這個(gè)例子我們看出,查詢(xún)優(yōu)化器希望盡可能選擇高效的執(zhí)行計(jì)劃,而執(zhí)行計(jì)劃緩存卻希望盡可能的重用緩存,這兩種機(jī)制在某些情況會(huì)產(chǎn)生沖突。

在下篇文章中,我們將會(huì)繼續(xù)來(lái)看由于執(zhí)行計(jì)劃緩存和查詢(xún)分析器的沖突,以及編譯執(zhí)行計(jì)劃所帶來(lái)的常見(jiàn)問(wèn)題和解決方案。

小結(jié)

本篇文章中,我們簡(jiǎn)單講述了查詢(xún)優(yōu)化器生成執(zhí)行計(jì)劃的過(guò)程,以及執(zhí)行計(jì)劃緩存的機(jī)制。當(dāng)查詢(xún)優(yōu)化器和執(zhí)行計(jì)劃緩存以某種不好的情況交匯時(shí),將產(chǎn)生一些問(wèn)題。在下篇文章中,我們會(huì)繼續(xù)探索SQL Server中的執(zhí)行計(jì)劃緩存。

以上內(nèi)容是小編給大家介紹的SQL Server中的執(zhí)行計(jì)劃緩存(上)的全部敘述,希望大家喜歡。

您可能感興趣的文章:
  • 淺析SQL Server 聚焦索引對(duì)非聚集索引的影響
  • MySQL中主鍵索引與聚焦索引之概念的學(xué)習(xí)教程
  • SQLSERVER中得到執(zhí)行計(jì)劃的兩種方式
  • SqlServer 執(zhí)行計(jì)劃及Sql查詢(xún)優(yōu)化初探
  • SQL Server中參數(shù)化SQL寫(xiě)法遇到parameter sniff ,導(dǎo)致不合理執(zhí)行計(jì)劃重用的快速解決方法
  • 淺析SQL Server中的執(zhí)行計(jì)劃緩存(下)
  • 強(qiáng)制SQL Server執(zhí)行計(jì)劃使用并行提升在復(fù)雜查詢(xún)語(yǔ)句下的性能
  • 淺析SQL Server的聚焦使用索引和查詢(xún)執(zhí)行計(jì)劃

標(biāo)簽:通遼 廣安 河北 福建 阿里 馬鞍山 南京 陜西

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《淺析SQL Server中的執(zhí)行計(jì)劃緩存(上)》,本文關(guān)鍵詞  淺析,SQL,Server,中的,執(zhí)行,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《淺析SQL Server中的執(zhí)行計(jì)劃緩存(上)》相關(guān)的同類(lèi)信息!
  • 本頁(yè)收集關(guān)于淺析SQL Server中的執(zhí)行計(jì)劃緩存(上)的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    扎兰屯市| 惠东县| 炉霍县| 长岭县| 鹿邑县| 日土县| 临澧县| 舒城县| 乃东县| 合水县| 岳西县| 南涧| 临江市| 澄江县| 穆棱市| 于田县| 绍兴县| 陇南市| 河曲县| 宁陵县| 龙州县| 海晏县| 基隆市| 交城县| 额敏县| 思茅市| 墨竹工卡县| 石台县| 榆中县| 阿克苏市| 兴仁县| 永泰县| 泽州县| 翁源县| 兰西县| 竹北市| 盘山县| 云霄县| 东明县| 洪雅县| 永寿县|