1. 程式人生 > >(譯)內存沈思:多個名稱相關的神秘的SQL Server內存消耗者。

(譯)內存沈思:多個名稱相關的神秘的SQL Server內存消耗者。

-c 開發人員 com 博文 hash join sum -s 並發執行 申請

原文:(譯)內存沈思:多個名稱相關的神秘的SQL Server內存消耗者。

原文出處:https://blogs.msdn.microsoft.com/sqlmeditation/2013/01/01/memory-meditation-the-mysterious-sql-server-memory-consumer-with-many-names/

對於多個不同名稱的內存消費者

你曾經是否想知道內存授予是什麽(Memory grants )?
什麽是查詢執行的保留(預定)內存(QE Reservations)?
以及查詢執行內存(Query Execution Memory)?
工作空間內存(Workspace memory),以及內存保留(譯者註:很多地方都將Reservations譯作“保留”,個人認為譯作“預定”一次更為容易理解,下文中預定同義與“保留”)


就好比生活中的許多事情一樣,簡而言之,一句話:所以的名字都指向SQL Server中的同一個內存消費者,
查詢期間的為了排序或者哈希操作(包括bulk copy和索引創建等)的內存分配
請允許我引申出來更大的話題,在一個查詢執行的過程中,這個查詢可能會從不同的"buckets" 或者 clerks中申請內存,(從不同地方申請內的差異)依賴於申請的內存用來做什麽。
例如,一個查詢在最初的解析和編譯時,他需要消費編譯或者是優化內存。一旦查詢語句編譯完成之後,他所申請的內存就會釋放,
同時其對應的查詢執行計劃會被緩存,為此,這個執行計劃會消費過程緩存內存,並且這塊內存一直被占用直到服務器重啟或者內存發生壓力,此時,查詢準備好執行。
如果查詢語句發生排序或者哈希操作(連接或者組合),那麽它首選會使用實現預定的內存用來存儲結果或者存儲Hash桶(的結果)
這個部分內存發生在查詢執行期間,具體會涉及到很多內存的名稱。


術語和故障診斷工具

讓我們回顧一下您可能會遇到的關於這個內存消耗者的不同類別。
同樣,所有這些描述了與相同內存分配相關的概念:

查詢執行內存 (QE Memory):
  這一項是用來強調在查詢執行過程中sort/hash內存使用這個事實,同時也是查詢執行期間的最大的內存消費部分。

查詢內存(QE)預定或者內存預定:
  當一個查詢需要sort/hash操作準備內存的時候,在執行期間,他將基於包含了sort或者hash運算的原始查詢計劃,執行一個內存保留請求(內存預定請求),
  然後查詢開始執行,他請求內存,同時sqlserver將會授予這個查詢部分或者是全部的內存請求,主要是依賴於服務器可用內存
  有一個叫做MEMORYCLERK_SQLQERESERVATIONS的內存clerk,這個內存計數器來保留這部分內存的分配的情況。

  ------------------------------譯者註---------------------------------
  這部分內存可以從sys.dm_os_memory_clerks中查詢出來,如下截圖是一個存在負載的服務器的服務器上的MEMORYCLERK_SQLQERESERVATIONS分配情況
  可見這部分內存還是不小的,這裏是有超過4GB的MEMORYCLERK_SQLQERESERVATIONS(服務器內存32GB)
  如果一個服務器沒有負載的時候,這個內存的使用是很低的,甚至可以是0

  技術分享

  --------------------------------------------------------------------------

內存授予(Memory Grants):
  當SQL Server給一個請求授予請求內存的時候,稱之為內存授予發生了,
  有一個性能計數器保留了已經有多少內存授予了請求內存:Memory Grants Outstanding
  另外一個計數器表明有多少查詢已經請求了sort/hash內存,但是出於等待狀態,因為查詢執行內存已經耗盡:Memory Grants Pending
  這兩個內存計數器顯示了內存授予和內存授予的不足,也就是說,一個單獨的查詢可能消耗4GB的內存去執行一個排序,但這兩種情況都不會被反映出來

  ------------------------------譯者註---------------------------------
  這部分內存可以理解為實時請求內存,SQL Server實時請求越多,這部分內存就越大,
  比如一個Session需要20MB的運行內存,同時運行10個Session就需要200MB,同時運行100個Session就需要2000MB,當然每個Session需要的運行內存都不一樣(memory grant),這裏僅僅是舉例
  可以從sys.dm_os_memory_clerks中查詢出來,如下截圖是一個存在負載的服務器的服務器上的MEMORYCLERK_SQLQERESERVATIONS分配情況
  可見這部分內存還是不小的,這裏是有超過4GB的MEMORYCLERK_SQLQERESERVATIONS(服務器內存32GB)
  如果一個服務器沒有負載的時候,這個內存的使用是很低的,甚至可以是0
  Memory Grants Outstanding某一個Session無法得到其申請的內存,處於等待內存狀態,
  SQL Server是一個自我調節(self tuning)引擎,這部分內存一般會預留的足夠,正常情況下不會發生因為sql的執行缺少內存而等待的情況
  凡事得分清楚輕重緩急,內存也一樣分為急需的和非急需的,不能那邊為了緩存數據而占據內存,而這邊連實時的請求都無法響應吧。
  舉個不恰當的例子,再窮,可以不買房不買車,總的留夠吃飯的錢吧。
  其他地方的內存可以存在壓力(比如數據緩存可能快速地被置換出內存),但是面對相應實時請求的內存,SQL Server留的還是比較充足的,個人很少見到因為Session等待內存而無法執行的情況。
  但是話不能反過來說,不能說沒有出現Memory Grants Pending,就說明內存沒有壓力。

技術分享

  技術分享

  ----------------------------------------------------------------------------

為了觀察單獨的請求和他們已經申請的內存,你可以查詢sys.dm_exec_query_memory_grants這個DMV
這個DMV顯示的是當前運行的查詢的內存授予情況,而非歷史情況
另外,你可以捕獲實際查詢執行計劃,查詢其一個叫做Query plan的XML節點,他包含了內存授予的大小,
如下示例:
<QueryPlan DegreeOfParallelism="8" MemoryGrant="2009216"
另外一個DMV是 sys.dm_exec_requests,包含了一個叫做granted_query_memory的8kb為單位的列,
比如一個1000的值,意味著1000*8kb,或者說是8000kb的內存授予

工作空間內存(Workspace Memory):

  這仍舊是另外一個描述同樣內存的項目,
  你經常會看到這個性能技術區授予Workspace Memory (KB),反映的是當前所有用來sort/hash操作的內存使用
  最大工作空間內存(Maximum Workspace Memory (KB))數量是自SQL Server啟動以來的最大的工作空間內存。
  在我看來,工作空間內存項目是一個用來描述sqlserver7.0個sqlserver2000的內存分配的一個遺留問題,在SQLServer2005以後已經作廢

資源信號量(Resource Semaphore):
  為了給這個概念增加更多的復雜性,SQL Server使用一個稱作信號量(semaphore)的線程同步對象來跟蹤有多少已經被授予了的內存。
  想法是這樣的:如果sqlserver用光了workspace memory/QE memory, 那麽使用out-of-memory錯誤來替代查詢執行失敗(不是直接反饋查詢失敗,而是等待內存)
  它會促使查詢等待可用內存,然後(得到可用內存之後)可以重新執行

  在這個過程中,Memory Grants Pending 性能計數器開始變得有意義。
  因此會在sys.dm_exec_query_memory_grants中生成wait_time_ms,granted_memory_kb = NULL, timeout_sec
  順便說一下,這個和編譯內存是在SQL Server中僅有的在內存不足的情況下,會執行等待內存的兩種內存。
  在其他情況中,查詢將會直接失敗並報出701錯誤--內存不足。

在SQL Server中另外一種等待類型也昭示著一個查詢是在等待內存授予--RESOURCE_SEMAPHORE
正如文檔中所說的,這種情況發生,一個查詢內存無法被立即授予歸結於其他並發執行的查詢。
頻繁的等待和等待時間可能預示這個過多的並發查詢,或者是過多的內存授予數量。
你可以在sys.dm_exec_requests這個DMW中觀察Session級別的等待

為什麽需要關註Memory Grants 或者 Workspace Memory 或者 Query Execution Memory或者不管你怎麽稱呼它。

過去幾年對於性能問題的診斷過程中,我發現這是最常見的內存相關問題之一,應用程序經常執行一些看起來相當簡單的查詢,
卻因為上執行大量的Sort或者hash操作而遭受到大量的性能破壞
那些查詢不僅僅是在執行過程中消耗大量的內存,並且也會導致其他查詢等待內存,這就是性能瓶頸。

使用上述我提供的工具(DWVs,性能計數器和實際執行計劃),你可以查出來哪個查詢是耗費了大量的內存,然後再考慮優化/重寫的可能性

Sort/Hash操作產生的情景

提及查詢重寫,這裏有一些可能會導致大量內存授予的情況

Sort操作產生的原因(包括但不限於以下幾種情況)
  ORDER BY (T-SQL)
  GROUP BY (T-SQL)
  DISTINCT (T-SQL)
  Merge join操作是優化器選擇的,其中合並聯接的一個輸入必須被排序


Hash Match操作產生的原因(包括但不限於以下幾種情況)
  JOIN (T-SQL) – 如果SQL選擇執行一個Hash操作,典型的就是缺少合理的索引導致的一個昂貴的join操作--hash join,觀察執行計劃
  DISTINCT (T-SQL) –Hash聚合可能會用來執行distinct操作,觀察執行計劃
  SUM/AVG/MAX/MIN (T-SQL)– 任何聚合操作可能會導致Hash 聚合,觀察執行計劃
  UNION – Hash聚合可能會用來做移除重復項


了解這些常見的原因可以幫助應用程序開發人員盡可能地消除對SQL Server的大量內存授予請求。
一如既往,基本的查詢調優開始於檢查查詢是否有適當的索引,以幫助它們減少讀取、盡可能減少或消除大型排序。

這裏有個內存授予的博文供參考 http://blogs.msdn.com/b/sqlqueryprocessing/archive/2010/02/16/understanding-sql-server-memory-grant.aspx

(譯)內存沈思:多個名稱相關的神秘的SQL Server內存消耗者。