所以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的内存瓶颈

    由文章前面所述的一些基本原理可以看出,由于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位SQL Server的问题

    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 ManagerTotal Server Memory计数器去找到SQL Server真实占用的内存。

 总结

    本文讲述了SQL Server对内存管理的基本原理和SQL Server对内存使用所分的部分,对于SQL Server性能调优来说,理解内存的使用是非常关键的一部分,很多IO问题都有可能是内存所引起的。