1. 程式人生 > >Sql Server 記憶體相關計數器以及記憶體壓力診斷

Sql Server 記憶體相關計數器以及記憶體壓力診斷

  在資料庫伺服器中,記憶體是資料庫對外提供服務最重要的資源之一,
  不僅僅是Sql Server,包括其他資料庫,比如Oracle,MySQL等,都是一類非常喜歡記憶體的應用.
  在Sql Server伺服器中,最理想的情況是Sql Server把所有所需的資料全部快取到記憶體中,但是這往往也是不現實的,因為資料往往總是大於可用的實體記憶體
  可以說記憶體是否存在壓力能夠直接決定資料庫能否高效執行,
  同時,如果記憶體出現壓力,同時也會影響到CPU的使用和儲存效能,可以說是一損俱損,具有連帶性。
  那麼,如何識別記憶體是否存在壓力,如何判斷一臺伺服器上是否存在記憶體瓶頸?

  Sql Server 2012之後,對記憶體的管理進行了大刀闊斧的改革,所有的記憶體管理都受Max Server Memory的控制
  如下截圖所示的最大伺服器記憶體設定(當然這個截圖是我本機上一個測試例項,這裡並不說明記憶體該怎麼配置)

而大多數的記憶體量化都涉及到Sql Server的Buffer Pool,一個內部的快取管理器,

/*
  20160525補充:
  Sql Server 2012之前的版本,
  對於Buffer Pool是儲存Data Cache的,
  另外一部分專用的記憶體稱之為Memory To Leave(Sql Server 2012之後,叫做Stolen Memory),
  這部分記憶體的使用也是一個非常大的話題,這裡暫不展開論述。
  32位作業系統下,這部分記憶體是Sql Server 啟動後直接初始化分配的,64位作業系統是直接跟作業系統申請,
  如果需要的Stolen Memory過大,同樣會“擠壓”Buffer Pool的記憶體
*/

  但是,Sql Server2012之後,所有記憶體的管理都受到Max Server Memory的控制。
  同時,Sql Server在執行的過程中,會將各種記憶體的引數情況記錄下來,這對我們去判斷Sql Server記憶體壓力有著非常重要的參考意義
  下面提到的部分計數器的就儲存在sys.dm_os_performance_counters這個系統檢視中

我們抽取其中最重要的幾個來做解釋說明:

  1. Page Life Expectancy
  2. Buffer Cache hit ratio
  3. Page reads / sec
  4. Page writes / sec
  5. Lazy writes / sec
  6. Total Server Memory
  7. Target Server Memory
  8. Paging File % Usage

需要注意的是,不能通過上述某一個值就武斷地斷定記憶體瓶頸,各個計數器之間是有一定的關係的,要結合多個值來做謹慎的分析判斷。

  • Page Life Expectancy

  Page Life Expectancy又簡稱位PLE,含義是記憶體頁面在記憶體中停留的平均時間,是記憶體壓力判斷的一個重要參考值
  在系統檢視sys.dm_os_performance_counters中可以查到,單位是秒.
  需要注意的是它不是指某一個page的最大值或者最小值,而是所有由所有頁面停留在buffer pool中的時間計算出來的一個平均值
  如果這個值越大,說明Sql Server在檢索資料時候直接從buffer pool中獲取資料的概率越大,
  如果Sql Server直接從buffer pool中檢索到資料,那麼就不用去磁碟中去查詢,因為直接從記憶體中獲取資料的效率要遠遠高出從磁碟中去獲取資料
  因為從記憶體中查詢資料的延遲是納秒級的,而從磁盤獲取資料的延遲是毫秒級的,這之間差了兩個數量級,
  可見從快取中獲取資料和從磁碟中獲取資料,對效能的影響有多大
  那麼PLE這個值多少位正常呢?我發現很多資料上多這個值都有誤解,說是300S,300S是在十多年前的一個參考值,
  是基於當時的伺服器記憶體受到4GB記憶體的限制的影響得到的,
  目前伺服器記憶體動輒超過100GB的情況下,用同樣的標準,顯然是不夠準確的,這個值的計算是跟具體的伺服器記憶體配置有關的
  具體我就不做進一步的解釋,可以參考如下連結

    一個可供參考的標準演算法是 Max Buffer Pool(GB)/4*300(S)
    這個值可以通過sys.dm_os_performance_counters 這個系統檢視直接查詢得出

select * 
from sys.dm_os_performance_counters 
where object_name like '%Buffer Manager%' and counter_name='Page life expectancy'

    比如你的服務記憶體是64G,分配給Sql Server最大記憶體(上述Max Server Memory)是60G
    那麼PLE的參考值就是60/4*300=4500S,大概是75分鐘,也就是說,最低限度是每75分鐘,記憶體中的資料跟磁碟做一次完整的交換
    如果你的伺服器上的PLE值長期低於計算出來的這個參考值,或者這個值在某個時間段內有非常明顯的變化,那麼你就需要注意記憶體是否存在瓶頸了
    如果你真的做過這方面觀察的話,這個值在不同環境中差別是非常大的
    當然對於測試伺服器,經常沒幾個人用,或者壓力非常小的伺服器,記憶體沒壓力或者伺服器根本沒有負載的情況下,快取進去的資料可能就一直存在於記憶體中
    這個值有可能非常大,達到幾萬秒都是有可能的
    不信我給你截個圖,呵呵

  

    當然對於壓力比較大的生產伺服器,即便是有幾十個GB的記憶體,這個值,也有可能小到幾十秒鐘,我所在的公司就是這個情況。
    所以,PLE的值是作為判斷記憶體是否存在瓶頸的最重要的指標之一。

  • Buffer Cache hit ratio

    Buffer Cache hit ratio就是快取命中率,字面上的解釋就是一個查詢過程中所需要的時候,直接從記憶體中讀取出來的比例佔所有資料的百分比
    鑑於表現出來的值受到其演算法的制約,反倒是在記憶體壓力診斷的時候並不具備太多的參考意義,
    既然Buffer Cache hit ratio不具備太多的參考意思,那麼為什麼把他放在這裡呢?
    因為這一個非常流行的引數,很多材料上都提到過這個引數
    很多材料上都介紹其閾值是90%,95%之類的參考值,其實都是錯誤的,
    其實真正觀察過的人,如下連結,早就有人有此疑問了,從PLE和Buffer hit ratio得出根本不一致的結論,
    有時候我們做學問還是要講究嚴禁的,不能人云亦云

    另外,真的很佩服老外,從本質上闡述了Buffer Cache hit ratio,能把學問做的這麼認真,真的不容易。

通過sql查詢快取命中率

SELECT 
  CAST(CAST((a.cntr_value * 1.0 / b.cntr_value)*100 as int) AS VARCHAR(20)) as BufferCacheHitRatio
FROM (
        SELECT * FROM sys.dm_os_performance_counters
        WHERE counter_name = 'Buffer cache hit ratio'
        AND object_name = CASE WHEN @@SERVICENAME = 'MSSQLSERVER'
        THEN 'SQLServer:Buffer Manager'
        ELSE 'MSSQL$' + rtrim(@@SERVICENAME) +
        ':Buffer Manager' END 
    ) a
CROSS JOIN
(
    SELECT * from sys.dm_os_performance_counters
    WHERE counter_name = 'Buffer cache hit ratio base'
    and object_name = CASE WHEN @@SERVICENAME = 'MSSQLSERVER'
    THEN 'SQLServer:Buffer Manager'
    ELSE 'MSSQL$' + rtrim(@@SERVICENAME) +
    ':Buffer Manager' END 
) b
  • Page reads(writes) / sec 

  這兩個計數器分別是對應的平均每秒鐘的物理讀/寫的資料量,這個計數器的是一個累計值,單位為page,而每個page又是8Kb,
  可以換算成一個基於kb或者mb位單位資料
  對於計數器中的類似累計值,並不妨礙我們通過計算得出某個時間間隔內的平均值。
  也就是說,
  對於 Page reads / sec,一個查詢在執行過程中,發現需要的資料不存在於buffer pool中,需要到磁碟上去查詢
  對於 Page writes / sec ,就是在面臨記憶體壓力的時候,將記憶體頁寫入磁碟來騰出記憶體空間
  上面說了,直接從buffer pool中,也就是記憶體中讀取資料和從磁碟中讀取資料的時間上的差別是巨大的,對效能的影響也是非常明顯的
  實際中我們也會經常遇到這種現象,有些SQL查詢語句,第一次執行比較慢,但是再次執行的時候,就相對快了很多,
  當然通過set statistics io 這個資訊也能發現有第一次存在物理讀的現象,這種速度上的差別,還是比較明顯的
  這兩個值可以通過如下sql查詢得到

select * 
from sys.dm_os_performance_counters 
where object_name like '%Buffer Manager%' and (counter_name='Page reads/sec' or counter_name='Page writes/sec' )

    如果一臺伺服器上經常發生大批量的物理性IO操作,你就要注意是否存在記憶體問題,
    因為經常性的大批量的物理IO會嚴重拖慢SQL的執行效率,理想情況下,這個值不應該過大,也有材料上說不能持續大於0,我個人覺得有點絕對了
    其實也沒有一個絕對的標準,只要這個值能夠穩定在一個較低的水平,沒有持續性的大批量資料的寫入(磁碟)於讀取(從磁碟載入記憶體),都可以接受
    相反,如果長期在一個高位水平,並且觀察到PLE不能穩定在參考值範圍內,說明記憶體可能存在瓶頸。

  • Lazy writes / sec

    Lazy writes 是每秒被緩衝區管理器的惰性編寫器(lazy writer)寫入的快取區的資料page資訊。
    Lazy writer是一個系統程序,用於批量重新整理記憶體中的髒頁到磁碟,並且將原來髒頁佔用的記憶體空間清理的一個動作。
    如果存在記憶體壓力,Lazy writer會被觸發,將髒頁和長時間沒有用到的計劃快取清理出記憶體,
    如果經常被觸發,那麼說明記憶體可能存在瓶頸
    需要注意的是,通過如下 sys.dm_os_performance_counters 查詢出來的Lazy writes/sec值是一個累計值
    但是這也不妨礙我們得出某一個時間間隔內發生的Lazy writes/sec的資料,相信聰明的你一定可以算出來 

select * 
from sys.dm_os_performance_counters 
where object_name like '%Buffer Manager%' and counter_name='Lazy writes/sec'

    對於髒頁以及老化的快取計劃,有其他機制去實現寫入磁碟儲存並清理器佔用的記憶體空間
    Lazy Write是在面臨記憶體壓力的情況下觸發的,
    如果某一個時間間隔內,Lazy Write持續不為零,就要結合PLE以及Page reads(writes) / sec 來判斷分析記憶體是否存在不夠用的情況了。

  說完PLE和Page reads(writes) / sec 以及Lazy writes / sec之後,就可以做一個小小的總結了

  上面說了,衡量記憶體瓶頸的時候,通常要結合多個值來做出判斷,
  如果你的PLE不在計算出來的參考值預期之內,同時又伴隨著大量的Page reads(writes) / sec
  那麼就幾乎可以斷定你的伺服器存在記憶體瓶頸了
  因為PLE達不到預期值,也就是說可能有大量所需要的資料不存在於快取中,
  而去讀這些資料,又要進行從磁碟上的物理讀取,那麼就會出現Page reads(writes) / sec 較高的現象
  物理讀取出來的資料要佔用快取空間(之後才能返回給查詢的客戶端),
  而原來快取空間中的資料是通過Lazy writes被清理出記憶體,這樣資料從磁碟進入快取,而快取中的資料又被清理出去,造成的結果就是PLE上不去
  所以結合這三個值的資訊,基本上就可以斷定你的記憶體是否存在瓶頸。

  當然除了上述三個計數器,還有其他更多的資訊去對記憶體做診斷,我們繼續。

  • Total Server Memory/Target Server Memory

    Total Server Memory是Sql Server記憶體管理器“已提交”記憶體,說白了就是已經佔用了的記憶體,
     而Target Server Memory則是Sql Server記憶體管理器可用的最大記憶體
    這兩個值也可用通過sys.dm_os_performance_counters 查詢出來

select * 
from sys.dm_os_performance_counters 
where object_name like '%Memory Manager%' and counter_name in ('Target Server Memory (KB)','Total Server Memory (KB)')

  當Total Server Memory小於Target Server Memory的時候,Sql Server還知道系統還有可用記憶體,在需要記憶體中的時候,直接跟系統申請,
  此時Total Server Memory會逐漸變大。
  但是,當Total Server Memory接近於或者等於Target Server Memory的時候,Sql Server會意識到已經用完了系統的可用記憶體,
  如果在需要記憶體的時候,系統已經無法繼續分配新的記憶體,它就需要清理已用的記憶體空間,將新清理出來的空間給新的資料使用
  這個似乎又要跟上面說的PLE以及lazy write聯絡上了,
  當然,系統記憶體空間往往是小於資料的空間的,比如有可能你的資料庫檔案大小是500GB,而記憶體之後32G或者64G,
  資料庫經過一段時間的執行後,Total Server Memory總是接近於或者等於Target Server Memory的,
  那麼我們說Total Server Memory和Target Server Memory的意義何在?
  上面說了,鑑於資料檔案中的資料往往都是大於可用實體記憶體的(當然極端例子也有,你資料庫只有2GB,記憶體32GB)
  資料很有可能不完全都能快取在記憶體中,但是最起碼,要快取持續到一定時間再去釋放空間(給新的資料使用),而不是不停地直接去讀磁碟,這就要求有一個度
  你不能說Total Server Memory總是接近於或者等於Target Server Memory,沒記憶體用了,清理記憶體是正常的
  如果快取75分鐘是正常的,
  發現Total Server Memory持續性接近或者等於Target Server Memory,而PLE明顯低於計算出來的參考值,低到幾分鐘甚至一兩分鐘,
  同時觀察到記憶體跟磁碟之間頻繁地、大量地物理性交換資料,這也說明,記憶體極有可能存在瓶頸。

  •  Paging File % Usage

  Paging File也即快取檔案,另外一個名字叫做虛擬記憶體,你一定聽說過,就是用拿物理磁碟空間當做記憶體空間使用,
  Windows系統的虛擬記憶體檔案一般是儲存在C盤的,一個叫pagefile.sys的檔案,預設是隱藏的
  如下截圖

  

  這裡先說明兩個問題:
  1,Sql Server會用到快取檔案嗎?
  答案是:會
  2,Sql Server能否控制使用實體記憶體還是page file?
  答案是:不能,一個windows上的應用,使用實體記憶體還是page file,是由作業系統決定的,應用程式本身無法決定自身去使用哪一部分記憶體

  那麼如何知道使用了多少快取檔案空間,通過sys.dm_os_sys_memory這個檢視可以查詢出來。
  當然我這個截圖是在我本機,看不出來有什麼特別大的使用了,一個欄位是total_page_file_kb,一個是available_page_file_kb
  顧名思義,總的減去可用的,就是已用的

  

   那麼,檔案快取跟記憶體瓶頸有什麼關係呢?
  應用程式對檔案快取的使用時不受自身因素控制的,完全是由作業系統來決定的,Sql Sever也不例外,檔案快取使用的多少當然也是由作業系統來排程
  檔案快取的使用多少能反映什麼?
  如果說檔案快取使用的越多,從側面可以反映出來伺服器上當前實體記憶體和實際需求記憶體之間的差距,當然這個差距越大,說明記憶體缺乏程度越高
  檔案快取的使用是受到Windows作業系統排程的,這一點Sql Server無法決定自己的快取資料是存放在屋裡記憶體中或者是page file中,
  這一點就是一個黑盒了,具體演算法我無從得知
  從實際測試來看,實體記憶體的消耗和page file的消耗是同步的,
  舉個例子,執行一個非常大的查詢,通過 sys.dm_os_sys_memory可以非常清楚地觀察到,
  在消耗實體記憶體的同時,也伴隨著虛擬記憶體的消耗,這兩者到底怎麼分配,或者這之間有什麼線性關係,我目前還不清楚,也希望有高人指點
  可以很明確地說,某些生產伺服器,因為缺乏實體記憶體,32GB的實體記憶體的機器,
  對於檔案快取的使用達到了一個非常高的程度(30多個GB),超過了實體記憶體本身
  這應該就是一種非正常狀態,不過這個值也沒有一個權威的資料,也希望有了解的可以留言貢獻
  當然獲取某些環境下有更大的檔案快取使用,我只是沒見過而已。
  根據page file的使用情況,發現如果大量使用page file,甚至超過了實體記憶體本身,
  可以大概瞭解到Sql Server伺服器實際所需記憶體與現有記憶體的差異程度。
  也可以在進行記憶體瓶頸判斷的時候,作為參考指標之一。

 總結

     林林總總闡述了上述幾個記憶體瓶頸壓力判斷指標,也僅僅是涉及到了一部分跟記憶體有關的計數器,當然包括但不限於上述幾個值。
    如果做記憶體瓶頸判斷,可以有更多的參考值,
    前文也說了,記憶體壓力下,
    Sql Server是一個具備自我調節(self tuning)的應用系統,各個計數值的值是具有一系列的相關性的,往往多個性能計數器會表現出來一些一致性的特徵
    比如記憶體不足的情況下:PLE上達不到預期值,Page reads(writes) / sec 又持續保持在一個較高的水平,同時伴隨著Lazy Writer / sec 持續性的發生
    如果有更多的其它參考的判斷指標,當然更具備說服力
    但是如果通過上述值,也能將記憶體的壓力是否存在瓶頸定位個八九不離十。
    對於其他的記憶體相關的計數器,有時間會繼續總結。
    其實說了這麼些相關內容,也僅僅是對Sql Server記憶體一個做了一個非常粗略的分析,當然也可以對各個部分的記憶體分類進行進一步細化的分析和論述。

本文粗淺第分析了判斷Sql Server記憶體瓶頸的一些知識點,尚有不足的地方還請指出,謝謝。希望能夠幫到各位對Sql Server感興趣的看官,共同學習。