深入SQL SERVER 2000的内存管理机制(三)

** 深入 ** ** SQL SERVER 2000 ** ** 的内存管理机制(三) ** ** **


** ** ** http://msdn.microsoft.com/data/default.aspx?pull=/library/en-us/dnsqldev/html/sqldev_01262004.asp **


** 内存区域 **

SQL Server 是分 2 块区域来组织内存分配,分别是 Bpool ( 缓冲池区 ) 和 MemToLeave (内存释放区),如果你使用 AWE 内存,那么实际上有第三个区: Windows AWE 支持的高于 3GB 的物理内存区。

缓冲池区是这 3 块内存区中最卓越的,是 SQL SERVER 最初分配的缓冲池供最初的数据页和索引页使用,并且被用来分配小于 8K 的内存。 MemToLeave 是由虚拟内存空间组成包括在用户内存空间没有被缓冲池区使用的内存空间之中。 Windows AWE 调用 3GB 以上内存空间的函数作为缓冲池区的扩展可以提供额外的内存空间缓存数据页和索引页。

当你启动 SQL SERVER 时,缓冲池区的上限是根据机器中物理内存推算或用户内存空间的大小。一旦缓冲池区的大小被确定,内存释放区就紧随其后 , 不至于被后面的缓冲池区保留部分划分成分散的碎片。然后缓冲池区又在内存释放区旁边,使用 32 块单独的保留区运行 DLL 文件和其他在缓冲池区被预定时 SQL SERVER 中使用的虚拟内存空间。当缓冲池区被预留后,内存释放区被释放。这段区域被 SQL SERVER 内部用来扩展 8K 的数据页和分配给其他外部应用 ( 就像 : 内存消费者是 SQL SERVER 主要引擎以外的 SQL SERVER 进程 ), 比如 :OLE DB providers,COM 对象等等 .

因此 , 当 SQL SERVER 已经启动 , Bpool (缓冲池区 ) 就被预留 , 但不提交 , 同时在该进程在虚拟内存空间内的 MemToLeave (内存释放区)其实是空闲区域 . 如果你通过性能分析器的 Virtual Bytes Perform 计数器 , 在 SQL Server 启动后看 SQL SERVER 的进程 , 你会看到它可以反映 Bpool ( 缓冲池区 ) 预留区 . 我看到人们有些惊慌 , 因为这个数值比较高 — 毕竟 , 它反映的不是本机器的总共物理内存就是最大的用户内存空间减去 MemToLeave (内存释放区) . 这是不用担心的 , 比较而言这只是保留区 , 没有提交的空间 . 就如我前面所述 , 保留空间只是地址空间 — 并没有真实的物理内存存储直到内存空间被提交 . 随着时间的过去 , 内存空间被提交 , Bpool (缓冲池区 ) 将会增加 , 知道该 SERVER 原始启动时确定的上限 .

** 监控SQL Server虚拟内存的使用 **

你可以通过 SQL Server:Buffer Manager\Target Pages Perform 计数器跟踪 Bpool ( 缓冲池区 ) 确定的最大空间 . 因为 SERVER 不同的部分需要内存 , Bpool (缓冲池区 ) 提交 8K 大小的页 ( 这是原始保留的直到提交的大小到达确定目标 ). 你可以通过 SQL Server:Buffer Manager\Total Pages Perform 计数器跟踪 Bpool ( 缓冲池区 ) 使用的提交虚拟内存 , 你可以通过 Pivate Bytes 计数器跟踪 SQL Server 进程使用的全部提交的虚拟内存 .

因为 , 大部分的 SQL Server 的虚拟内存的使用是来自于 Bpool ( 缓冲池区 ), 通过上面 2 个计数器可以知道 . 一般而言增长和平稳是一前一后地 .( 请牢记 : 当应用程序启动了对 AWE 的支持 , Pivate Bytes 计数器不能反映总体 SQL Server 内存使用情况 ). 如果 Total Pages Perform 计数器是水平的而 Pivate Bytes 计数器是向上倾斜的 , 这一般表示正在从 MemToLeave (内存释放区)分配新的内存 . 这个分配过程会正常结束 — 例如 : 在 SERVER 中分配相关联的线程堆栈作为附加的工作线程 , 这也有可能是一个内部测 COM 对象或 XPROC 的内存泄漏 . 如果一个程序因为 MemToLeave (内存释放区)耗尽而用完虚拟内存空间 , 这是由于内存泄漏或内存过度消费 .( 或者在 MemToLeave (内存释放区)中最大空闲块降低到默认线程堆栈 0.5MB 以下 ), 这样 SERVER 就不能产生新的工作线程 , 即使在 sp_configure max worker threads 的值没有到 . 在这种情况下 , 如果 SERVER 需要产生新的工作线程来执行一个工作请求 — 比如 : 处理一个对 SERVER 的新的连接请求 , 这些工作将会延迟 , 直到 SERVER 可以产生新的线程或其他的线程可以使用 . 这样 , 在有足够的 MemToLeave (内存释放区)释放或其他的工作线程可以有效的处理连接之前 , 系统 会阻止一个用户连接到 SERVER, 因为这个连接会超时中止 (time out).

** 内存分配器 **

在 SERVER 中一个内存的消费者初始化一个内存分配器 , 首先是产生一个内存对象来管理这些请求 . 当这个对象来分配这些请求 , 他在 SERVER 的内存管理器中 , 从 Bpool ( 缓冲池区 ) 或 MemToLeave (内存释放区)来履行这些请求 . 如果这些请求小于 8K, 这些请求通常在 Bpool ( 缓冲池区 ) 分配 . 如果请求需要 8K 或以上的内存空间 , 这些请求通常在 MemToLeave (内存释放区)分配 . 因为一个单独的内存对象可以用来执行多次内存分配 . 所以有可能一次内存分配正好在 8K 以下 ( 包括管理对象的消费 ) 的请求被分配在 MemToLeave (内存释放区) . 在 SQL Server 的处理空间中 , 内存消费者通常是内部的 . 换句话说 , 这些内存消费者和对象是 SQL Server 自己的规范需要消耗内存来执行任务 , 但也不一定都这样 . 也存在一些外部的消费者 , 就像我前面锁说的 . 通常 , 这些外部的内存消费者调用正常的 Win32 API 内存函数来分配和管理内存 , 并且从 MemToLeave (内存释放区)分配内存空间 , 非常明显这是 SQL Server 程序中唯一有效的区域 . 可是 XPROCS 有特殊的异常处理 , 当一个 xproc 调用 **Open Data Services(ODS) srv_alloc API ** 函数 , 这完全和其他的内存消费者一样 . 一般而言 srv_alloc API 函数从 Bpool ( 缓冲池区 ) 申请小于 8K 的内存 , 对于大的内存从 MemToLeave (内存释放区)分配 .

** 内存管理者 **

当 SERVER 运行时 , 内存管理者检查物理内存的剩余有效容量 , 以保证 WINDOWS 和其他的应用程序可以可以平稳的运行 . 这个有效内存的大小在 4MB 和 10MB 直接变化 .( 在 WINDOWS 2003 中更接近 10MB) 并且这个基于系统内核加载和 Bpool ( 缓冲池区 ) 中的页生命周期 . 如果 SERVER 上的有效物理内存空间在这个阀值以下 ,SERVER 会减少提交 Bpool ( 缓冲池区 ) 页来收缩内存物理存储的使用 ( 假设动态内存分配是激活的 ). 内存管理者也保证提交的内存页在指定的时间点后空闲 , 这样在接受到一个新的分配请求时 , 就不再需要等待内存分配 . 通过 ” 空闲 ”, 我的意思是 : 内存页提交后 , 但没有被使用 . 没有使用的提交 Bpool ( 缓冲池区 ) 页可以通过一个空闲页清单来跟踪 . 这样所有得新得页都来自空闲页清单 , 内存管理者从 Bpool ( 缓冲池区 ) 预留区提交更多得页直到全部得预留区被提交 . 你看 : Process:Private Bytes Perfmon 计数器缓慢增长 ( 通常是成直线的 ) 就是这个原因 .

在多 CPU 系统中 , 没一个都有自己单独的空闲页清单 , 当一个空闲页被要求响应一个应用程序的请求 , 首先在当前的 CPU 的空闲页清单中满足分配的空间被检查 , 然后是其他 CPU 的空闲页清单 . 这样更好的利用每个处理器的本地缓存 , 提高了在多处理器的 SERVER 上提高稳定性 . 你可以通过 SQL Server:Buffer Partition Perform 对象监视指定的 Bpool ( 缓冲池区 ) 区 , 你可以可以通过 SQL Server:Buffer Manager\Free Pages Perform 计数器监视所有的 Bpool ( 缓冲池区 ) 区 .

因此在 SQL SERVER 整个运行过程中 ,SQL SERVER 的内存处理器 ( 无论内存线程管理器或同其他的线程服务 ) 监视系统的内存使用状态 , 确认合理的剩余空闲物理内存的数量给其他的系统和预留合理的空闲页响应新的内存请求 . 当 SERVER 使用 AWE 内存 , 有些内存状态必需改变 . Bpool (缓冲池区 ) 区开始通过 SERVER 的物理内存中获得和锁定内存 , 内存锁的数量的变化是根据服务器最大内存是否设置了 . 如果配置了 , Bpool (缓冲池区 ) 区试图根据最大的服务器内存锁住相应数量的内存 . 如果没有配置 , Bpool (缓冲池区 ) 区会锁住接近 128MB 内存的所有物理内存 , 只有少量的区域留给其他处理 . 然后 Bpool ( 缓冲池区 ) 区使用高于 3G 的物理内存 (AWE 内存 ) 用作运行数据和索引的页文件 . Bpool (缓冲池区 ) 区根据需要映射物理内存和虚拟内存 , 因此可以被 32 位的指针引用 .

** 概述 **

你已经知道了 :SQL Server 内存管理器是一个难点 . 理解一个程序是如何分配和管理内存是了解一个程序如何工作的基础 . 内存是非常重要的资源 , 它的有效利用是一个可靠应用程序设计的基本因素 , 懂得一个应用程序内存管理机制会是你的程序设计如虎添翼 .

一个开发者 , 他是如何影响你呢 ? 理解 SERVER 的内存管理机制赋予你如何写一个高效的应用程序和解决一些和内存相关联问题的洞察力 . 比如说 , 在一个提高客户端连接速度的调试中 , 你提高 SQL Server 的默认网络包的大小为 8K., 立即 , SQL SERVER 立刻开始在 ERROR LOG 中写错误信息 , 提示预留虚拟内存在 MemToLeave (内存释放区)区有问题 . 在看到这样的信息后你立刻会知道这个改变至少是问题的一部分 , 因为你知道分配 8K 或更多的内存是在 MemToLeave (内存释放区)区 . 这样 SQL Server 连接相关联的缓存也是来自这个区域 , 因为你已经配置网络包的大小太高了 . 配置默认的网络包适合于 .NET frameworkSQLClinet Provider 8KB. 这种情况并不象听起来那么合理 . 实际上 , 这是非常常见的问题 , 由于 **MemToLeave ** ( 内存释放区)缺乏内存空间而引起的 , 因为网络包的大小太大 , 至少有些部分是这样的 .

同时 , 了解 SQL Server 划分内存的方法可以帮助你了解你定制在 SQL Server 中运行的代码是否在系统资源的临界值 , 比如数据缓存 . 比如说 , 你建立一个扩展存储过程调用 SRV_PRO() <SPAN style="FONT-FAM

Published At
Categories with 数据库类
Tagged with
comments powered by Disqus