原文地址:https://www.cnblogs.com/CareySon/archive/2012/08/16/HowSQLServerManageMemory.htmlhtml
理解SQL Server對於內存的管理是對於SQL Server問題處理和性能調優的基本,本篇文章講述SQL Server對於內存管理的內存原理。sql
對於計算機來講,存儲體系是分層級的。離CPU越近的地方速度愉快,但容量越小(如圖1所示)。好比:傳統的計算機存儲體系結構離CPU由近到遠依次是:CPU內的寄存器,一級緩存,二級緩存,內存,硬盤。但同時離CPU越遠的存儲系統都會比以前的存儲系統大一個數量級。好比硬盤一般要比同時代的內存大一個數量級。數據庫
圖1.計算機存儲體系express
所以對於SQL Server來講,正常的生產系統所配置的內存一般不能裝載全部數據,所以會涉及到二級存儲,也就是磁盤。磁盤做爲現代計算機系統中最後的機械存儲部件,讀取數據須要移動磁頭(具體關於磁盤的原理,能夠看我以前寫的一篇文章),而且因爲數據庫所訪問的數據每每是隨機分佈在磁盤的各個位置,所以若是頻繁的讀取磁盤須要頻繁的移動磁頭,這個性能將會十分底下。windows
由計算機體存儲體系結構能夠知道,計算機對於全部硬盤內數據的操做都須要首先讀取到內存,所以利用好內存的緩衝區而減小對磁盤IO的訪問將會是提高SQL Server性能的關鍵,這也是本篇文章寫做的出發點之一。緩存
因爲SQL Server過去一直面向是中小型企業市場的緣由,SQL Server存儲引擎被設計成一個不須要太多配置就能使用的產品,從而減小了部署成本,但這也是不少人一直詬病的微軟開放的配置過少。而對於SQL Server如何使用內存,幾乎沒有直接能夠配置的空間,僅僅開放的配置只有是否使用AWE,以及實例佔用的最大或最小內存,如圖2所示。服務器
圖2.SQL Server可控控制內存的選項函數
而對於具體的SQL Server如何使用內存,例如分配給執行計劃緩存多少,分配給數據buffer多少,這些都沒法經過配置進行調控。這也是不少其它技術的開發人員對於使用微軟技術的開發人員充滿優越感的緣由,而在我看來,雖然SQL Server提供可控配置的地方不多,可是不少地方均可以在通曉原理的狀況下進行「間接」的配置。這也須要了解一些Windows的原理。性能
SQL Server存儲引擎自己是一個Windows下的進程,因此SQL Server使用內存和其它Windows進程同樣,都須要向Windows申請內存。從Windows申請到內存以後,SQL Server使用內存粗略能夠分爲兩部分:緩衝池內存(數據頁和空閒頁),非緩衝內存(線程,DLL,連接服務器等)。而緩衝池內存佔據了SQL Server的大部份內存使用。緩衝池所佔內存也就是圖2最大最小內存所設置的,所以sqlservr.exe所佔的內存有可能會大於圖2中所設置的最大內存。spa
還有一點是,SQL Server使用內存的特色是:有多少用多少,而且用了之後不釋放(除非收到Windows內存壓力的通知)。好比我所在公司的開發服務器,在幾乎沒有負載的時候來看內存使用,如圖3所示。
圖3.SQL Server 進程的內存使用
能夠看到CPU在0負載的時候,內存卻佔據了13個G。這實際上是在以前的使用SQL Server向Windows申請的內存一直沒有釋放所致。
具體SQL Server可以使用多少內存是由如下幾個因素決定的:
1.物理內存的大小
2.所安裝Windows版本對於內存的限制(好比windows server 2008標準版限制最大內存只能使用32GB)
3.SQL Server是32位或64位
4.如圖2所示配置SQL Server對於內存的使用量
5.SQL Server的版本(好比express版只能用1G內存)
SQL Server OS對於內存的分配分爲三個層級,依賴關係如圖4所示。
圖4.SQL Server OS內存依賴關係
Memory Node
首先最底層的是Memory Node,Memory Node的做用是使得分配內存由Windows移交到SQL Server OS層面執行。每一個SQL Server實例一般都只擁有一個Memory Node,Memory Node的多寡只取決於NUMA構架的硬件配置。咱們經過 DBCC MEMORYSTATUS 能夠看到Memory Node的一些信息,如圖5所示。
圖5.查看Memory Node信息
咱們能夠看出 ,按照申請內存大小分類,能夠分爲兩部分
1.申請小於等於8KB爲一個單位的內存,這些內存被用於緩存。(圖5中的SinglePage Allocator)
2.申請大於8KB爲一個單位的內存,這些內存稱爲Multi-Page(或MemToLeave)(圖5中的MultiPage Allocator)
對於爲何叫MemToLeave,被稱爲MemToLeave的緣由是因爲SQL Server雖然大部份內存被用於緩衝區,但還須要一些連續的內存用於SQL CLR,linked server,backup buffer等操做,32位SQL Server在啓動實例時會保留一部分連續的虛擬地址(VAS)用於進行MultiPage Allocator。具體保留多少能夠用以下公式計算:
保留地址=((CPU核數量-4)+256)*0.5MB+256MB,一般在384MB左右。
Memory Clerk
讓咱們再來看Memory Clerk,Memory Clerk用於分配內存,用於將Allocate出去的內存進行分類,能夠簡單的進行以下語句,如圖6所示.
圖6.按照Memory Clerk的類別進行分類
注意:由圖4能夠看到,Memory Clerk只是分配內存的一部分,另外一部分是數據緩存(Buffer Pool)
在開始講述Buffer Pool以前,首先想講一下虛擬內存。
在Windows中每一個進程都有一個虛擬內存(Virtual Address Space VAS),32位系統是2的32次方,也就是4G,這4G被Windows劃爲兩部分,一部分是Windows使用,另外一部分纔是應用程序使用。虛擬內存並非實際的物理內存,而是對於物理內存的映射,當物理內存不存在虛擬內存指向的內容時,產生缺頁中斷,將一部分頁面置換出內存,而後將須要的部分從硬盤讀到內存,關於這塊,能夠讀我以前寫的一篇文章:淺談操做系統對內存的管理。
所以Buffer Pool的做用是緩衝數據頁,使得將來讀取數據時減小對磁盤的訪問。
這個Buffer Pool這部分就是圖2中設置最大最小服務器內存所佔用的空間。這個最小值並不意味着SQL Server啓動時就能佔用這麼多內存,而是SQL Server Buffer Pool的使用一旦超過這個值,就不會再進行釋放了。
在DBCC MEMORYSTATUS 其中有一部分咱們能夠看到Buffer Pool的信息,如圖7所示。
圖7.Buffer Pool的相關信息
在SQL Server實例啓動時,Buffer Pool所保留的VAS地址空間取決於多個因素:包括實際的物理內存和SQL Server是32位或是64位(這個限制32位是4G,還要劃一半給Windows和減去MemToLeave空間),而對於實際上SQL Server所使用的物理內存,能夠經過以下語句查看,如圖8所示。
圖8.查看Buffer Pool所使用物理內存
Buffer Pool會按照須要不斷的提出內存申請。Buffer Pool若是須要,Buffer Pool會不斷消耗內存,直到Windows通知SQL Server內存太低時,Buffer Pool纔有可能釋放內存,不然Buffer Pool佔據了內存不會釋放。
另外值得注意的一點是,Buffer Pool所分配的頁面和SQL Server OS頁面大小是一致的,也就是8192字節,當SQL Server其它部分須要向」Buffer Pool」借內存時,也只能按照8k爲單位借,而且這部份內存在物理內存中是不連續的,這聽上去像是Buffer Pool內存管理自成體系,能夠這麼理解,由於Buffer Pool 不使用任何SQL Server的page allocator,而直接使用virtual或AWE SQLOS's的接口。
因此SQL Server所佔用的內存能夠用這個公式粗略估算出來: buffer pool佔用的內存+從buffer pool借的頁佔得內存+multiPageAllocator分配的非buffer pool內存,如圖9所示。
圖9.能夠近似的估算出sql server所佔的內存
Memory Object
menory object本質上是一個堆,由Page Allocator進行分配,能夠經過sys.dm_os_memory_objects這個DMV進行查看,這個DMV能夠看到有一列Page_Allocator_Address列,這列就是Memory Clerk的標識,代表這個Memory Object是由哪一個Memory Clerk進行分配的。
由文章前面所述的一些基本原理能夠看出,因爲32位的SQL Server使用的是VAS進行地址分配,所以尋址空間被限制在4GB,這4GB還要有一半分給Windows,使得Buffer Pool最多隻能用到2G的內存,這使得32位SQL Server即便有多餘的物理內存,也沒法使用。
解決辦法之一是經過減小Windows默認佔用的2G到1G,使得SQL Server可使用的內存變爲3G。這個能夠經過在Windows Server 2008中的命令行鍵入 BCDEdit /set設置increaseuserva選項,設置值爲3072MB,對於Windows Server 2003來講,須要在boot.ini中加上/3gb啓動參數。
另外一種辦法是使用AWE(Address Window Extension)分配內存。AWE經過計算機物理地址擴展(Physical Address Extension PAE),增長4位,使得32位的CPU尋址範圍增長到2的36次方,也就是64GB。基本解決了尋址範圍不夠的問題。
VirtualAlloc和AllocateUserPhysicalPages
VirtualAlloc和AllocateUserPhysicalPages是SQL Server向Windows申請內存所使用的方法。在默認狀況下,SQL Server所須要的全部內存都會使用VirtualAlloc去Windows申請內存,這種申請是操做系統層面的,也就是直接對應的虛擬內存。這致使一個問題,全部經過VirtualAlloc分配的內存均可以在Windows面臨內存壓力時被置換到虛擬內存中。這會形成IO佔用問題。
而使用AllocateUserPhysicalPages所申請的內存,直接和更底層的頁表(Page Table)進行匹配,所以使用這個方法申請的內存不會被置換出內存。在32位SQL Server的狀況下,經過開啓AWE分配內存,buffer pool中的data cache部分將會使用這個函數,而MemToLeave部分和Buffer Pool中的另外一部份內存(主要是執行計劃緩存)依然經過VirtualAlloc進行內存分配。
所以在開啓經過AWE分配內存以前,SQL Server首先須要對應的權限,不然就會在日誌中報錯,如圖10所示。
圖10.開啓AWE卻沒有開啓對應權限報錯
咱們能夠在組策略裏設置啓動SQL Server的帳戶擁有這個權限,如圖11所示。
圖11.鎖定內存頁(Lock Page In Memory)
64位Windows基本已經不存在上述的內存問題,可是依然要注意,在默認狀況下,64位的SQL Server使用的依然是VirtualAlloc進行內存分配,這意味着全部分配的內存都會在Windows面臨壓力時將頁置換出去,這極可能形成抖動(Buffer Pool Churn),這種狀況也就是SQL Server Buffer Pool中的頁不斷的被交換進硬盤,形成大量的IO佔用(能夠經過sys.dm_exec_query_memory_grants這個DMV查看等待內存的查詢),所以64位SQL Server將Buffer Pool中的Date Page經過AllocateUserPhysicalPages來進行內存分配就能避免這個問題。與32位SQL Server不一樣的是,64位SQL Server並不須要開啓AWE,只需開啓如圖11所示的「Lock Page In Memory」就好了。
但這又暴漏出了另外一個問題,由於SQL Server鎖定了內存頁,當Windows內存告急時,SQL Server就不能對Windows的內存告急作出響應(固然了Buffer Pool中的非data cache和MemToLeave部分依然能夠,但每每不夠,由於這部份內存相比Data Cache消耗很小),由於SQL Server的特性是內存有多少用多少,所以頗有可能在沒法作出對Windows低內存的響應時形成Windows的不穩定甚至崩潰。所以開啓了」Lock Page In Memory」以後,要限制SQL Server Buffer Pool的內存使用,前面圖2中已經說了,這裏就再也不細說了。
還有一個問題是當Buffer Pool經過AllocateUserPhysicalPages分配內存時,咱們在任務管理器中看到的sqlservr.exe佔用的內存就僅僅包含Buffer Pool中非Data Cache部分和MemToLeave部分,而不包含Data Cache部分,所以看起來有可能形成sqlservr.exe只佔用了幾百兆內存而內存的使用是幾十G。這時咱們就須要在Perfmon.exe中查看SQL Server:Memory Manager\Total Server Memory計數器去找到SQL Server真實佔用的內存。
本文講述了SQL Server對內存管理的基本原理和SQL Server對內存使用所分的部分,對於SQL Server性能調優來講,理解內存的使用是很是關鍵的一部分,不少IO問題都有多是內存所引發的。