对于数据库引擎来讲,内存是一个性能提高的重要解决手段。把数据缓存起来,能够避免在查询或更新数据时花费多余的时间,而这时间一般是从磁盘获取数据时用来等待磁盘寻址的。把执行计划缓存起来,能够避免重复分析执行计划时带来额外的CPU及各类资源的开销。经过在内存中开辟查询内存空间,能够迅速地完成排序、哈希等计算,达到快速返回运算结果的目的。若没有足够的内存空间,数据库引擎将没法快速地响应用户的请求。算法
SQL Server存储引擎自己是一个Windows下的进程,因此SQL Server使用内存和其它Windows进程同样,都须要向Windows申请内存(经过VirtualAlloc之类的API向Windows申请内存)。sql
内存芯片提供的物理存储空间,能被CPU直接访问,访问速度快,易丢失。内存性能指标GB/s,ns(纳秒),前者是吞吐量,后者是响应时间。磁盘性能指标MB/s,us(微秒),从二者对比就能看出内存访问速度是远优于磁盘的。数据库
物理内存容量是有限的,若是全部进程都直接使用有限的物理内存,那新的进程将没法为他们找到任何物理内存,那么物理内存将容易成为瓶颈。因此Windows会授予每一个进程一个虚拟地址空间(Virtual Address Space,VAS),经过VAS创建应用程序与物理内存的桥梁。windows
是指一个应用程序可以申请访问的最大地址空间。VAS做为中间的抽象层的,不是全部的请求都直接映射到物理内存,它首先映射到VAS,而后映射到物理内存。
而两个进程能够共用一个VAS,而VAS的大小取决于CPU架构,具体请看下面表格:api
OS Type缓存 |
Kernel Model服务器 (内核模式)网络 |
User Model数据结构 (用户模式)架构 |
Total |
32位系统 |
2GB |
2GB |
4GB |
64位系统 |
8TB |
8TB |
16TB |
VAS有两种内存模式,Kernel Model和User Model。Kernel Model下的VAS是供Windows系统进程使用,而User Model下的VAS是供用户进程使用。
由表格可知,32位Windows系统应用程序能够访问最大2GB的VAS,64Windows位系统能够访问最大8TB的VAS。这意味着在32位Windows系统中一个word文档进程跟一个SQL Server进程能获得最大2GB的VAS是同样的。所以,从理论上讲,这意味着任何应用程序进程在32位Windows系统上都将共享最大限度的2 G的VAS。
VMM是负责把物理内存在系统中全部须要内存的进程之间做共享,必要时会从VAS回收物理内存,把数据存储到页面文件上面去,保证数据永不丢失。当进程须要内存时,VMM会从页面文件中查找数据,并将这数据写入一部分空闲内存当中,而后将新页面映射到须要操做的VAS当中。
SQL Server 2012对内存管理这块跟SQL Server 2008仍是有比较大的区别的,参考一些资料,下面咱们来看看二者具体架构。
SQL Server 2008 R2: SQL Server 2012:
为了更加清楚了解Buffer Pool,咱们先来了解下 SQL Server的所须要的内存有哪些,其中包括SQL Server服务(sqlserver.exe)和其它一些组件所占用的内存,例如SQL Server代理程序(sqlagent.exe), SQL Server复制代理程序、SQL Server报表服务(ReportingServicesService.exe)、SQL Server Analysis Services(msmdsrv.exe)、SQL Server Integration Services(MsDtsSrvr.exe),和SQL Server 全文搜索(msftesql.exe)。
在一台运行SQL Server的服务器上,运行着sqlserver服务(sqlserver.exe)和其它一些组件。在sqlserver服务(sqlserver.exe)获取到的内存中,又分为2大块:一部分为Buffer Pool,另外一部分为非Buffer Pool,旧称MemToReserve(默认sqlserver.exe给它预留了256MB)。下表为这两部份内存各自的用途:
SQL Server 进程所占内存 |
||
Buffer Pool EXEC sp_configure N'min server memory EXEC sp_configure N'max server memory |
非Buffer Pool(即MemToReserve) (默认为256MB), 能够sqlserver.exe启动时加-g参数,预留足够内存(预留内存大小=256MB+工做线程数*512KB) |
|
Buffer Pool中主要存放以前查询中的数据页,和索引页。而后根据它自已的算法,自动清理过时过访问或效率低下的页。 |
SQL Server工做线程 |
占用很少 |
分布式查询引用的OLE DB访问接口 |
如操做连接服务器 |
|
备份还原 |
维护计划或者T-SQL备份恢复 |
|
扩展过程 |
如sp_或sys开头的系统存储过程,sp_OACreate 存储过程 |
|
多页的分配器SQL Server内存管理器 |
如.net framework程序(它们链接sqlserver的网络包大小为8K, sqlserver默认网络包大小为4k) |
|
.DLL文件 |
|
|
SQL Server CLR的Microsoft COM对象 |
这块内存是<=8kb的存储,适用于sql server 2008及之前,属于Buffer Pool缓冲池来分配。有存储数据页面,Consumer功能组件。
这块内存是>8kb的存储,适用于sql server 2008及之前,不属于Buffer Pool缓冲池来分配, 有存储Consumer功能组件, 第三方代码, Threads线程。
这个适用于sql server 2012及以上,整合了single-page,multi-page统称any size page。
它来统一响应SQL Server 内部各类组件内存申请的请求。由于这个缘由,在SQL Server 2012里面,max server memory 再也不像之前的版本那样,只控制buffer pool的大小,也包括那些大于8kb 的内存请求。也就是,max server memory可以更准确地控制SQL Server 的内存使用了。
从内存架构咱们能够看到有page reservation需预先申请的内存,有momory objects从windows api申请的内存,有clr第三方申请的内存。
(1)Database Cache(数据页面缓冲区)
存放数据页面的缓冲区。SQL Server数据库里的数据都是以8KB为一个页面存储。当有用户须要使用到这个页面上存储的数据时,SQL Server会把整个页面都调入内存,供用户使用。因此8KB是数据访问的最小单元。当用户修改了某个页面上的数据时,SQL Server会在内存中将这个页面修改,可是不会马上将这个页面写回磁盘,而是等到后面的Checkpoint或Lazy Write的时候集中处理。
(2)各种Consumer
SQL Server的不少功能组件,都必需要申请内存来完成它们的任务。这些统称为“Consumer”。常见有以下:
Connection:SQL Server为每一个链接分配一个数据结构,存储关于这个链接的信息。另外,还会分配一个输入缓冲池,缓冲客户端发来的指令;一个输出缓冲池,存放SQL Server返回的结果,等待客户端取走。
General:一组大杂烩。包括语句的编译、范式化、每一个锁数据结构、事务上下文、表格和索引的元数据等。
Query Plan:语句和存储过程的执行计划。和Database Cache相似,若是SQL Server没有内存压力,它就会保留每个生成的执行计划,供之后的用户重用,减小Comlile的消耗。因此Query Plan也会是一块比较大的内存使用区域。
Optimizer:SQL Server在生成执行计划的过程当中须要消耗的内存。
Utilities:像BCP、Log Manager、Parallel Queries、Backup等比较特殊的操做须要的内存。
(3)线程内存
SQL Server会为每一个进程内的每一个线程分配0.5MB的内存,以存放线程的数据结构和相关信息。
(4)第三方代码申请的内存(COM,XP...)
在SQL Server的进程里,会运行一些非SQL Server自身的代码。例如,用户定义的CLR或者Extended Stored Procedure代码,Linked Server须要加载的数据连结驱动,调用SQL Mail功能须要加载的MAPI动态库等。这些代码也会申请内存,会算在SQL Server本身都不知道。
有些SQL Server内存的申请方式,是预先Reserve一块大的内存,而后在使用的时候一小块一小块地Commit。而另外的内存申请则直接从空间里Commit。在SQL Server里,把后一种方式叫Stolen。
在SQL Server里,对Database Cache,SQL Server会先Reserve,再Commit。其余的全部内存使用,基本都是直接Commit,都是“Stolen”。要重申的是,Stolen内存也是正常使用的内存,不是泄漏掉的内存。
之因此要把这两种分开,是由于SQL Server不会对Stolen的内存使用AWE功能。也就是说,AWE扩展出去的内存,只能用来存放Database Cache。其余内存还要在原来的那2GB里想办法。
对于SQL Server本身申请的内存,有两种内存申请单位。
小于等于8KB一个单位内存申请,SQL Server就分配一个8KB页面。全部这些页面都集中管理,这块内存被称为Buffer Pool。一次一个页面的这种分配称为Single Page Allocation。
对于大于8KB为单位的内存申请,SQL Server把它们集中在另一个区域,称为Multi-Page Allocation(旧称MemToLeave)。而这种分配称为Multi-Page Allocation。
类型 |
Database Cache |
Consumer |
3rh Party Code |
Threads |
Reserved/Commit |
是 |
通常不是 |
通常不是 |
不是 |
Stolen |
不是 |
是 |
是 |
是 |
Buffer Pool (Single Page) |
全部 |
绝大部分 |
没有 |
没有 |
MemToLeave(Multi-Page) |
没有 |
一小部分 |
全部 |
全部 |
这里的一个例外是运行在SQL Server进程里的CLR代码所申请的内存。这部份内存像第三方代码同样,也是使用MemToLeave的内存。可是,CLR可能也会用Reserve-Commit的方式申请内存。因此MemToLeave的内存也并非都是Stolen的。
根据SQL Server内存架构图,咱们能够知道,在2012版本上,Single Page Allocation跟Multi-Page Allocation合并为Any Size Page Allocation了。而max server memory控制的不可是 Buffer Pool内存大小,而是全部大于等于小于8KB的内存请求。
如图:
好比我设置最小服务器内存为8G,从新启动下SQL Server (MSSQLSERVER)服务,再使用dmv来查看当前实例的总内存空间,以及占用内存空间:
--Target Server Memory (KB)最多能申请的内存量
--Total Server Memory (KB)目前使用了多少内存量
SELECT counter_name, ltrim(cntr_value*1.0/1024.0/1024.0)+'G'
AS memoryGB FROM sys.dm_os_performance_counters
WHERE counter_name like '%target%server%memory%'or counter_name like '%total%memory%'
从查询结果能够看到当咱们在SQL Server设置最小服务器内存为8G的时候,给SQL Server分配了多少内存,它就占用多少多少内存,从而达到性能最佳。
这是我初次学习SQL Server性能调优方面的知识,谢谢博友花阴偷移指点。不少知识点方面可能理解有误差,但愿各个路过大神指点一二。
参考文献:
Microsoft.SQL.Server企业级平台管理实践
SQL Server 2012 内存管理 (memory management) 改进