性能調優11:查詢統計


數據庫引擎的工作流程可以歸納為接收請求、執行請求和返回結果。數據庫引擎每接收到一個新的查詢請求(Query Request),查詢優化器就會執行以下工作流程:

  • 編譯請求:對TSQL語句進行語法解析,編譯請求,生成TSQL語句表示的邏輯結構。
  • 查詢優化:根據TSQL語句的邏輯結構,生成多個預估的執行方案,並根據統計信息,評估每個預估方案的開銷,選擇開銷最低的方案作為最優方案。
  • 執行計划:根據最優方案生成執行計划,也就是把TSQL語句中的邏輯操作符轉變為物理操作符,把執行計划傳遞給存儲引擎,並把執行計划緩存到內存中。
  • 響應請求:存儲引擎執行查詢計划,記錄每個查詢的執行信息,最后把查詢的結果返回到客戶端。

把執行計划存儲到內存的目的是為了復用執行計划,減少編譯查詢請求的時間消耗和CPU消耗。當數據庫引擎再次接收到相同或相似的查詢請求時,數據庫引擎探測到該請求的執行計划已經被緩存,那么就會跳過編譯請求的過程,直接復用已經緩存的執行計划。

數據庫引擎並不是把查詢計划永久保存在內存中,而是會根據內存的壓力,智能地剔除一些創建時間早、復用頻次少的執行計划。為了實現計划緩存的精准清理,數據庫引擎需要對查詢和查詢計划進行定位和統計,定位通過請求的語句句柄和計划句柄來實現,清理通過查詢統計來實現。

一,語句句柄和計划句柄

數據庫引擎要實現查詢計划的復用,必須能夠識別查詢已經執行過,這就需要對查詢語句進行標記;查詢的執行計划也會被標記,這就需要用到兩個唯一值:

  • sql_handle:用以唯一標識一段TSQL文本(Batch或SP),TSQL文本存儲在SQL Manager Cache(SQLMGR)中。
  • plan_handle:用於唯一標識一個已編輯的查詢計划,查詢計划存儲在計划緩存(Plan Cache)中。

sql_handle和plan_handle是如何生成的?

  • 對於ad hoc查詢,sql_handle是基於整體的SQL Text生成的哈希值;如果一個batch包含多個TSQL語句,那么多個TSQL語句作為一個整體,batch中的查詢字句擁有相同的sql_handle值,但是有不同的偏移量。
  • 對於執行的SP、觸發器或函數等數據庫對象,sql_handle是由database ID 和 object ID 派生的哈希值。
  • plan_handle是由整體(批處理或SP)生成的已編譯計划派生的哈希值。

sql_handle和plan_handle 之間具有1對多的關系。一個sql_handle 能夠生成多個查詢計划,對應多個plan_handle,但是每個plan_handle只能對應一個sql_handle 。sql_handle對於每一個batch都是唯一的,但是,如果執行batch的條件發生改變,比如set 選項發生變化,那么數據庫引擎在執行同樣的batch時,會生成新的執行計划,產生新的plan_handle,但是sql_handle不變。想要了解更詳細的信息,請閱讀《2.0 Sql_Handle and Plan_Handle Explained》。

1,SQL句柄

sql_handle是一個token,用於唯一標記查詢文本所屬的batch或sp,把sql_handle傳遞給 sys.dm_exec_sql_text()動態管理函數,並結合偏移 statement_start_offset和statement_end_offset,可以抽取出單個查詢的SQL文本。

函數 sys.dm_exec_sql_text(sql_handle | plan_handle)用於獲得整個Batch的TSQL文本,由於TSQL文本都是以nvarchar(max)類型存儲的,一個nvarchar是2個字節,因此,一般情況下,字節偏移量都是2的倍數。

2,計划句柄

plan_handle是一個token,是整個Execution Plan的哈希值,用於唯一標識一個batch或sp的執行計划,把plan_handle傳遞給sys.dm_exec_query_plan(plan_handle)動態管理函數,可以獲取整體(batch或sp)的showplan。

3,查詢計划(query plan)

查詢計划是指查詢語句的顯示計划(showplan),動態管理視圖 sys.dm_exec_query_plan 返回以XML格式表示的showplan,它只能返回整個batch執行的showplan,不能單獨查看某一個子句的執行計划。要想查看單個子句的執行計划,可以通過動態管理視圖 sys.dm_exec_text_query_plan 來實現,該視圖返回以文本格式表示的showplan:

sys.dm_exec_query_plan(plan_handle)

sys.dm_exec_text_query_plan   
(   
    plan_handle   
    , { statement_start_offset | 0 | DEFAULT }  
    , { statement_end_offset | -1 | DEFAULT }  
) 

對於文本查詢計划,需要指定特定的語句的偏移statement_start_offset 和 statement_end_offset,才能顯示單個子句的showplan。

二,抽取查詢語句

動態管理視圖 sys.dm_exec_query_stats 緩存的是單個查詢語句的執行計划,而sql_handle指向的是整個Batch或SP的句柄值,因此,在該視圖中,可能存在多個相同的sql_handle。

為了獲得單個查詢語句的文本,必須通過偏移量從整體(Batch語句)中抽取,偏移量的單位是字節,字節數量從0開始:

  • statement_start_offset:語句開始偏移的字節序號
  • statement_end_offset:語句結束偏移的字節序號,-1 表示TSQL文本的末尾

把sql_handle傳遞給 sys.dm_exec_sql_text()動態管理函數,並結合偏移 statement_start_offset和statement_end_offset,可以抽取出單個查詢的SQL文本,抽取查詢語句的腳本是:

select substring(st.text 
                ,qs.statement_start_offset/2+1,
                ( case when qs.statement_end_offset = -1 then len(convert(nvarchar(max), st.text))
                        else (qs.statement_end_offset - qs.statement_start_offset)/2
                    end 
                )
        ) as individual_query
        ,st.text as entire_query
from sys.dm_exec_query_stats qs
outer apply sys.dm_exec_sql_text(qs.sql_handle) as st

三,查詢的統計數據

數據庫引擎會把每一個查詢請求的執行信息保存起來,例如,查詢的文本,查詢等待的時長,執行的時間,消耗的資源等,並對這些信息進行匯總和統計,這些匯總之后的數據就是查詢統計,存儲到內存結構 DMV:sys.dm_exec_query_stats中。在該視圖中,每一行數據都表示一個查詢語句的統計數據。

請求的執行信息都經過匯總之后,存儲到DMVsys.dm_exec_query_stats中,從該統計數據中,可以找出對性能影響最大的查詢請求,由於該DMV存儲的是累加值,在使用數據之前,一定要關注記錄的開始時間:

  • creation_time:計划編譯的時間
  • last_execution_time:最近一次計划開始執行的時間

這兩個時間表示查詢計划的第一次執行和最后一次執行的時間戳。

1,查看語句級別的統計數據

執行計划的重編譯次數,執行查詢的總時間,邏輯讀和物理讀的次數等計數器,是觀察查詢執行情況的重要指標:

  • plan_generation_num:表示執行計划產生的數量,表示同一個TSQL文本重新編譯的次數;
  • creation_time :計划編譯的時間
  • execution_count:計划執行的次數
  • total_elapsed_time和max_elapsed_time:查詢計划完成的總時間,單詞elapsed是指單個語句執行的總時間,包括 waiting的時間或 CPU工作(worker)的時間,單位是微秒(us),一微秒是千分之一毫秒(ms)
  • total_worker_time 和 max_worker_time:CPU工作的總時間和最大時間,單位是微秒(us)
  • total_logical_reads和max_logical_reads:查詢計划執行的邏輯讀的總次數;
  • total_logical_writes和max_logical_writes:查詢計划執行的邏輯寫的總次數;
  • total_physical_reads和 max_physical_reads:查詢計划執行的物理讀的總次數;
  • total_rows和max_rows:查詢返回的數據行的總數量
  • total_dop和max_dop:並發執行的並發度的累加和
  • total_grant_kb和max_grant_kb:該查詢計划收到的預留授予內存(reserved memory grant)的總量,單位是KB
  • total_used_grant_kb和max_used_grant_kb:該查詢計划使用的預留授予內存(reserved memory grant)的總量,單位是KB
  • total_ideal_grant_kb:該查詢計划預估的理想授予內存(ideal memory grant)的總量,單位是KB
  • total_splils、max_spills和min_spills:查詢計划在完成一次執行時,出現頁溢出的總頁數;

以下腳本用於查看執行計划在單個語句級別上的平均數據,並按照平均執行時間排序,獲取 top 111 的數據:

select top 111 
    qs.execution_count,
    qs.total_rows/qs.execution_count as avg_rows,
    qs.total_worker_time/qs.execution_count/1000 as avg_worker_ms,
    qs.total_elapsed_time/qs.execution_count/1000 as avg_elapsed_ms,
    qs.total_physical_reads/qs.execution_count as avg_physical_reads,
    qs.total_logical_reads/qs.execution_count as avg_logical_reads,
    qs.total_logical_writes/qs.execution_count as avg_logical_writes,
    qs.creation_time,
    qs.plan_generation_num,
    --st.text as entire_query,
    substring(st.text,
            qs.statement_start_offset/2 + 1,      
            ( case when qs.statement_end_offset = -1 
                        then len(convert(nvarchar(max), st.text))
                else (qs.statement_end_offset -qs.statement_start_offset)/2
              end)
            ) as individual_query
from sys.dm_exec_query_stats qs 
cross apply sys.dm_exec_sql_text(qs.sql_handle) as st 
order by avg_elapsed_ms desc

2,查看存儲過程級別的查詢統計

對於緩存的存儲過程,數據庫引擎把SP相關的統計數據緩存在視圖:sys.dm_exec_procedure_stats 中,每一行數據都表示一個SP的統計數據:

select top 111
    db_name(ps.database_id) as db_name
    ,ps.database_id
    ,object_schema_name(ps.object_id,ps.database_id)+'.'+object_name(ps.object_id,ps.database_id) as proc_name
    ,ps.type_desc as proc_type
    ,ps.cached_time
    ,ps.execution_count
    ,ps.total_worker_time/ps.execution_count/1000 as avg_worker_ms
    ,ps.total_elapsed_time/ps.execution_count/1000 as avg_elapsed_ms
    ,ps.total_physical_reads/ps.execution_count as avg_physical_reads
    ,ps.total_logical_reads/ps.execution_count as avg_logical_reads
    ,ps.total_logical_writes/ps.execution_count as avg_logical_writes
from sys.dm_exec_procedure_stats ps
where ps.database_id<32767
order by avg_elapsed_ms desc

對於database_id 為 32767,這個id是資源數據庫(Resource Database)預留的ID,一般情況下,用戶創建的數據庫ID都會小於該數值。

四,顯示被緩存的計划

函數 sys.dm_exec_query_plan 以XML格式返回指定batch或SP的查詢計划,參數是plan_handle,這意味着,函數返回的是整個語句(Batch或SP)的showplan,XML格式是可視化的,也可以返回文本格式的showplan。

select top 111 
    qs.execution_count,
    qs.total_rows/qs.execution_count as avg_rows,
    qs.total_worker_time/qs.execution_count/1000 as avg_worker_ms,
    qs.total_elapsed_time/qs.execution_count/1000 as avg_elapsed_ms,
    qs.total_physical_reads/qs.execution_count as avg_physical_reads,
    qs.total_logical_reads/qs.execution_count as avg_logical_reads,
    qs.total_logical_writes/qs.execution_count as avg_logical_writes,
    qs.creation_time,
    qs.plan_generation_num,
    st.text as entire_query,
    substring(st.text,
            qs.statement_start_offset/2 + 1,      
            ( case when qs.statement_end_offset = -1 
                        then len(convert(nvarchar(max), st.text))
                else (qs.statement_end_offset -qs.statement_start_offset)/2
              end)
            ) as individual_query,
    qp.query_plan
from sys.dm_exec_query_stats qs 
cross apply sys.dm_exec_sql_text(qs.sql_handle) as st 
outer apply sys.dm_exec_query_plan(qs.plan_handle) as qp
order by avg_elapsed_ms desc

五,計划的統計信息

動態管理視圖:sys.dm_exec_cached_plans 中,每一個行存儲一個查詢計划,通過該視圖,可以查看已緩存的查詢計划、查詢文本、緩存計划占用的內存、緩存計划復用的次數等信息。

select cp.refcounts
    ,cp.usecounts
    ,cp.size_in_bytes
    ,cp.cacheobjtype
    ,cp.objtype
    ,st.text as batch_sql
    --,cp.plan_handle
from sys.dm_exec_cached_plans cp
outer apply sys.dm_exec_sql_text(cp.plan_handle) st

六,存儲過程的統計信息

對於緩存的存儲過程,sys.dm_exec_procedure_stats 返回聚合的性能統計數據,每一行都代表一個緩存的存儲過程計划,

select db_name(p.database_id) as dbname
    ,o.name as proc_name
    ,p.type_desc
    ,p.cached_time
    ,p.execution_count
    ,p.total_worker_time
    ,p.max_worker_time
    ,p.total_physical_reads
    ,p.max_physical_reads
    ,p.total_logical_writes
    ,p.max_logical_writes
    ,p.total_logical_reads
    ,p.max_logical_reads
    ,p.total_elapsed_time
    ,p.max_elapsed_time
    ,p.total_spills
    ,p.min_spills
    ,p.max_spills
    ,p.plan_handle
    ,cp.bucketid
    ,cp.refcounts
    ,cp.usecounts
    ,cp.size_in_bytes
    ,pn.query_plan
from sys.dm_exec_procedure_stats p
inner join sys.objects o 
    on p.object_id=o.object_id
inner join sys.dm_exec_cached_plans as cp
    on p.plan_handle=cp.plan_handle
outer apply sys.dm_exec_query_plan(p.plan_handle)  as pn

 

 

 

 

 

參考文檔:

Execution Related Dynamic Management Views and Functions (Transact-SQL)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM