SQL Server調優系列進階篇(查詢語句運行幾個指標值監測)

前言html

上一篇咱們分析了查詢優化器的工做方式,其中包括:查詢優化器的詳細運行步驟、篩選條件分析、索引項優化等信息。sql

本篇咱們分析在咱們運行的過程當中幾個關鍵指標值的檢測。數據庫

經過這些指標值來分析語句的運行問題,而且分析其優化方式。緩存

經過本篇咱們能夠學習到調優中常常利用的幾個利器!post

廢話少說,開始本篇的正題。性能

技術準備學習

數據庫版本爲SQL Server2008R2,利用微軟的一個更簡潔的案例庫(Northwind)進行分析。測試

 

利器1、IO統計優化

經過這個IO統計能爲咱們分析出當前查詢語句所要掃描的數據頁的數量。這裏面有幾個重要的概念,咱們依次分析。url

方法很簡單,一行代碼搞定:

SET STATISTICS IO ON

來看個例子

SET STATISTICS IO ON
GO
SELECT * FROM Person.Contact

這裏能夠看到這個語句對於數據表的操做次數,基於數據頁的掃描項。

所謂的數據頁就是數據庫的底層數據存儲方式,SQL Server以數據頁的形式存儲錶行數據。每一個數據頁爲8K,

8K=8192字節-96字節(頁頭)-36字節(行偏移)=8060字節

也就說一個數據頁存儲的純數據內容爲8060字節。

咱們依次來解釋上面出現幾個讀取的概念:

邏輯讀

表示處理查詢所須要訪問頁的總數。也就是說要完成一個查詢語句須要讀取的數據頁的總數。

這裏的數據頁有可能來自內存,也有可能來自硬盤讀取。

物理讀

這個就是說來自硬盤讀取的數據頁數。咱們知道SQL Server每次都會將讀取的數據頁儘量存在於內存中,以方便下一次直接讀取,提高讀取速度。

因此在這裏關於存儲於內存中的數據頁下次訪問的機率,提出了一個指標:緩存命中率

緩存命中率=(邏輯讀—物理讀)/邏輯讀

提出這個指標的提出其實就是爲了衡量內存中緩存的數據頁的有效性。好比:假如緩存與內存中的數據頁就使用一次就不使用了,對於這種就應該及時從內存中清除掉,畢竟對於內存資源來講是很是昂貴的。應該用它來緩存命中率高的數據頁。

預讀

預讀其實就是SQL語句在優化的時候預先讀取到內存中的數據頁數。這個預先讀取的數據頁是提早評估出來的,也就是上一篇咱們文章中介紹的查詢優化器要作的事情。

固然,這些預讀的數據頁有時候不是全部的都要用到,可是它基本能涵蓋到查詢用到的數據頁。

這裏要提示一下,預讀數據是經過另一個線程進行讀取的和語句優化線程非用同一線程,並行運行,目的是快速獲取數據,提高查詢獲取的速度。

從這個指標咱們能夠分析出不少問題,來舉個例子:

咱們新添加一張測試表,腳本以下

--執行下面腳本新生成一張表
SELECT * 
INTO NewOrders
FROM Orders
GO
--新增長一列
ALTER TABLE NewOrders
ADD Full_Details CHAR(2000) NOT NULL DEFAULT 'full details'
GO

而後利用以下腳原本看下這張表的大小

EXEC sp_spaceused NewOrders,TRUE
GO

咱們能夠看到這張表數據頁的總大小爲2216KB,咱們知道一頁爲8KB,能夠推斷出這個表的數據頁有:

2216(數據頁總大小)/8(一個數據頁大小)=277頁

也就是說這個數據表有277個數據頁。

固然,咱們也能夠經過以下DMV視圖來查看該頁的數據頁數

SELECT * 
FROM SYS.dm_db_index_physical_stats
(DB_ID('Northwind'),object_id('NewOrders'),NULL,NULL,'detailed')

通過上面的分析,

咱們能夠推測,在查詢這張表作全表掃描的時候,理論的數據頁的邏輯讀數就應該爲277次

經過以下語句驗證下

--先清空緩存數據,生產機慎用
DBCC DROPCLEANBUFFERS

SET STATISTICS IO ON

SELECT * FROM NewOrders

我去...

這裏的邏輯讀取爲1047頁,和咱們上面的推斷277頁不相符...擦...神馬緣由!!!

這裏就是咱們要分析的數據頁Forwarded record現象形成的。由於咱們在新創建的表,在後面新添加的一列數據:Full_Details,類型爲CHAR(2000)的數據列,當數據行中的變長列增加使得原有頁沒法容納下數據行時,數據將會移動到新的頁中,並在原位置留下一個指向新頁的指針,這就是所謂的: Forwarded record

 

咱們能夠經過以下DMV視圖,查看該表的Forwarded Record造成的頁有多少

SELECT * 
FROM SYS.dm_db_index_physical_stats
(DB_ID(N'Northwind'),object_id('NewOrders'),NULL,NULL,'detailed')

糾正一下:上圖的770數據頁爲Forwarded Record頁,非拆分頁的概念(感謝院友 wy123 指出)。

看到了,這裏的Forwarded Record頁爲770頁,那麼咱們就能夠推測出咱們的邏輯讀數量來了

277(原數據頁)+770(Forwarded Record頁)=1047頁

因此上面的咱們的問題就分析出緣由了。

咱們經過此表也展現了一個Forwarded Record頁的問題:會影響查詢性能。

解決的方式不少種,最簡單的方式就是重建彙集索引。

CREATE CLUSTERED INDEX orderID_C ON NewOrders(OrderID)
GO
DROP INDEX NewOrders.orderID_C
GO
SET STATISTICS IO ON
SELECT * FROM NewOrders
GO

經過IO統計項,除了能夠分析出上面的Forwarded Record頁形成的碎片外,更重要的地方使用來對比不一樣查詢語句之間的讀取次數,經過下降讀取的次數來優化語句。

 

關於預讀的狀況,咱們在前面已經分析了,其數據時經過另一個線程在T-SQL查詢語句優化的時候進行數據的預加載。

因此這個線程在預讀數據的時候實際上是有一個參考值的,根據這個參考值讀取出來的數據才能保證大部分數據是有用的,也就是提升上面提到的緩存命中率。

關於這個參考值,我分析了下,實際上是分爲兩中狀況分析的。

 

首先、若是是數據表爲堆表,SQL Server獲取的方式只能經過全表掃描了。而此方式爲了不重複讀取,增長消耗,因此一次的預讀並不是讀取一個數據頁,

而是一段物理上的連續64個頁

來看聯機叢書的官方解釋:

預讀機制容許數據庫引擎從一個文件中讀取最多 64 個連續頁 (512KB)。該讀取做爲緩衝區高速緩存中相應數量(多是非相鄰的)緩衝區的一次散播-彙集讀取來執行。若是此範圍內的任何頁在緩衝區高速緩存中已存在,當讀取完成時,所讀取的相應頁將被放棄。若是相應頁在緩存中已存在,也能夠從任何一端「裁剪」頁的範圍。

因此,若是咱們的表在物理上不是連續頁,那麼讀取次數就很差怎麼肯定了。

咱們來看個堆表的例子

SET STATISTICS IO ON
--新建個測試表
SELECT * INTO NewOrders_TEST FROM NewOrders
SELECT * FROM NewOrders_TEST

這裏預讀的次數爲8次,因此我估計底層的數據頁確定不是連續的。因此形成了多出了3次。

咱們能夠DBCC IND()進行查詢下,來驗證下個人推斷。

DBCC IND('Northwind','NewOrders_TEST',1)

數據信息比較多,我將其粘貼到Excel中,而後作了一個折線圖,其中塗掉的部分實際上是沒有數據頁的,因此不會產生一次讀取。

關於讀取順序標示的也有點問題,不過肯定的總數確定是8次.....

但願這種方式,各位看官能看懂了...但願我也表述明白了。

 

其次、若是表非堆表,也就是說存在彙集索引項,那麼好了,SQL Server很輕鬆的找到了它預讀的參考依據:統計信息。

而且,咱們知道數據以B-Tree數存儲,讀取的數據頁都存在與葉子節點。因此基本沒有了什麼連續讀取的感念。

一個葉子節點就是一個數據頁,一個數據頁就是一次預讀。

來看個例子:

咱們將上面的表添加上彙集索引項,再一次清空緩存,執行查詢,腳本以下

CREATE CLUSTERED INDEX NewOrders_TESTIndex ON NewOrders_TEST(OrderID)
GO
SELECT * FROM NewOrders_TEST

這裏添加了彙集索引,SQL Server彷彿一下看到了救星,根據統計信息,預讀數據就能夠。

因此若是統計信息有錯誤,就形成了預讀的亂讀取....而後嚴重下降了緩存命中率.....而後嚴重增長了內存中換出換入的速度....增長了CPU....

好了,我們繼續文章,上面咱們提到的這個預讀數據行,能夠在以下DMV中查到。

SELECT * 
FROM SYS.dm_db_index_physical_stats
(DB_ID(N'Northwind'),object_id('NewOrders_TEST'),NULL,NULL,'detailed')

從這個DMV視圖中能夠看到這種表統計信息爲277個數據頁,因此造成了277次預讀。

可是,事實這個數據表是279頁,也就是說統計的信息有問題,形成了少讀讀取了2個數據頁,而爲了彌補這個統計過失就出現了2次物理讀,從新從硬盤中獲取。 

 

利器2、時間統計

關於時間統計這個很簡單,就是統計T-SQL執行語句執行時間項,包括CPU佔用時間、語句編譯時間、語句執行總時間等項。

使用方法也很簡單,一行代碼

SET STATISTICS TIME ON

經過這個參數,能夠分析出以上信息,其做用主要是用來對比查詢語句調優中的執行時間,咱們的目標就是下降執行時間。

舉例:咱們經過開啓時間統計,來對比下,上面的查詢語句,在第一次運行和之後運行(數據已經緩存)的時間對比,瞭解下緩存的重要性

再次執行的時間

緩存追蹤(補充於2014年12月25日)

固然咱們也能夠再深刻一點,若是想查看該部分數據在內存中緩存的明細,能夠經過以下DMV腳本查看

SELECT * FROM sys.dm_os_buffer_descriptors
WHERE DB_NAME(database_id)='Northwind'
AND page_type='DATA_PAGE'
ORDER BY page_id 

也能夠經過該DMV分析出各個庫在內存中佔據的大小比例,腳本以下:

--清除緩存
dbcc dropcleanbuffers
--查看緩存內容中在內存大小
SELECT COUNT(*)*8/1024 as 'Cached Size(MB)'
       ,CASE database_id 
        WHEN 32767 THEN 'ResourceDB'
        ELSE DB_NAME(database_id)
        END AS 'Database'
FROM sys.dm_os_buffer_descriptors
GROUP BY DB_NAME(database_id),database_id
ORDER BY 'Cached Size(MB)' DESC

通過此次查詢,這張表已經所有緩存到內存裏了,由於整張表總共就2MB的大小

 

 

文章已經有點長度了...先到此吧。

關於調優內容太普遍,咱們放在之後的篇幅中介紹,有興趣的能夠提早關注。

 

參考文獻

  • 微軟聯機叢書讀取頁
  • 參照書籍《SQL.Server.2005.技術內幕》系列

 

有問題能夠留言或者私信,隨時恭候有興趣的童鞋加入SQL SERVER的深刻研究。共同窗習,一塊兒進步。

 

文章最後給出前面幾篇的鏈接,如下內容基本涵蓋咱們平常中所寫的查詢運算的分解,看來有必要整理一篇目錄了.....

SQL Server調優系列基礎篇

SQL Server調優系列基礎篇(經常使用運算符總結)

SQL Server調優系列基礎篇(聯合運算符總結)

SQL Server調優系列基礎篇(並行運算總結)

SQL Server調優系列基礎篇(並行運算總結篇二)

SQL Server調優系列基礎篇(索引運算總結)

SQL Server調優系列基礎篇(子查詢運算總結)

 -----------------如下進階篇-------------------

SQL Server調優系列進階篇(查詢優化器的運行方式)

 

若是您看了本篇博客,以爲對您有所收穫,請不要吝嗇您的「推薦」。

相關文章
相關標籤/搜索