时间:2021-07-01 10:21:17 帮助过:28人阅读
这两天在看DB2的内存管理的内容,看的很是模糊,有以下问题不明白,请教
【导读】本文将向您讲解 DB2 内存使用的基础,以及共享内存和私有内存的概念。这些内容同时适用于 32 位和 64 位的系统。
简介
理解 DB2 如何使用内存,可以防止过度分配内存,并有助于对内存的使用进行调优,从而获得更好的性能。
本文将向您传授
DB2 内存使用的基础,以及共享内存和私有内存的概念。这些内容同时适用于 32 位和 64 位的系统。虽然对于 64
位系统有一些限制,但是在未来的一段时间内还不大可能触及这些限制。因此,我们将焦点放在影响 32 位系统的内存限制,并对之进行详细的讨论。
我们首先讨论一般情况下
DB2 如何使用内存,接着讨论内存管理如何随着平台(AIX、Sun、HP、Linux 和 Windows)的不同而变化,以及它们对 DB2
的影响。最后,我们将给出一些实际生活中客户处境/问题以及他们的解决方案的有意义的例子。本文的内容适用于 DB2 version 8。
DB2 内存结构概述
图 1中说明了 DB2 内存结构。这种内存结构在所有平台上都是一致的。 注意:在多分区环境中,下面的图适用于多分区实例中的每个分区。
图 1 - DB2 内存结构
DB2 在 4 种不同的内存集(memory set)内拆分和管理内存。这 4 种内存集分别是:
实例共享内存(instance shared memory)
数据库共享内存(database shared memory)
应用程序组共享内存(application group shared memory)
代理私有内存(agent private memory)
每种内存集由各种不同的内存池(亦称堆)组成。图 1 也给出了各内存池的名称。例如, locklist是属于数据库共享内存集的一个内存池。 sortheap是属于代理私有内存集的一个内存池。
我们将详细讨论每一种内存集。
实例共享内存
每个
DB2
实例都有一个实例共享内存。实例共享内存是在数据库管理器启动(db2start)时分配的,并随着数据库管理器的停止(db2stop)而释放。这种内存集用于实例级的任务,例如监控、审计和节点间通信。下面的数据库管理器配置(dbm
cfg)参数控制着对实例共享内存以及其中个别内存池的限制:
实例内存( instance_memory)。
监视器堆( mon_heap_sz):用于监控。
Audit Buffer( audit_buf_sz):用于 db2audit 实用程序。
Fast Communication buffers ( fcm_num_buffers):用于分区之间的节点间通信。仅适用于分区的实例。
instance_memory参数指定为实例管理预留的内存数量。默认值是
AUTOMATIC。这意味着 DB2 将根据监视器堆、审计缓冲区和 FCM 缓冲区的大小计算当前配置所需的实例内存数量。此外,DB2
还将分配一些额外的内存,作为溢出缓冲区。每当某个堆超出了其配置的大小时,便可以使用溢出缓冲区来满足实例共享内存区内任何堆的峰值需求。在这种情况下,个别堆的设置是
软限制的,它们可以在内存使用的峰值期间进一步增长。
如果
instance_memory被设置为某一个数字,则采用 instance_memory与 mon_heap_sz、
audit_buf_sz和
fcm_num_buffers的和之间的较大者。这时,对实例内存就施加了一个硬性的限制,而不是软限制。当达到这个限制时,就会收到内存分配错误。出于这个原因,建议将
instance_memory的设置保留为
AUTOMATIC。
如果 instance_memory被设为 AUTOMATIC,则可以使用下面的命令来确定它的值:
db2 attach to instance_name(其中 instance_name是实例的名称)
db2 get dbm cfg show detail
下面的输出表明有 42 MB 的内存被预留给实例共享内存集(10313 页 * 4096 字节/页):
Size of instance shared memory (4KB) (INSTANCE_MEMORY) = AUTOMATIC(10313) AUTOMATIC(10313)
instance_memory参数只是设置了实例共享内存的限制。它并没有说出当前使用了多少内存。要查明一个实例的内存使用情况,可以使用 DB2 内存跟踪器工具 db2mtrk。例如,
db2start
db2mtrk -i -v
Memory for instance
FCMBP Heap is of size 17432576 bytes
Database Monitor Heap is of size 180224 bytes
Other Memory is of size 3686400 bytes
Total: 21299200 bytes
上面的例子表明,虽然预留给实例共享内存集的内存有
42 MB,但在 db2mtrk运行时只用到了大约 21 MB。注意:在某些情况下,db2mtrk
显示的大小会大于指定给配置参数的值。在这种情况下,赋予配置参数的值被作为一种软限制,内存池实际使用的内存可能会增长,从而超出配置的大小。
数据库共享内存
每个数据库有一个数据库共享内存集。数据库共享内存是在数据库被激活或者第一次被连接上的时候分配的。该内存集将在数据库处于非激活状态时释放(如果数据库先前是处于激活状态)或者最后一个连接被断开的时候释放。这种内存用于数据库级的任务,例如备份/恢复、锁定和
SQL 的执行。
图2展示了数据库共享内存集内的各种内存池。括号中显示了控制这些内存池大小的配置参数。
图 2 - DB2 数据库共享内存
完整的绿色方框意味着,在数据库启动的时候,该内存池是完全分配的,否则,就只分配部分的内存。例如,当一个数据库第一次启动时,不管 util_heap_sz的值是多少,只有大约
16 KB 的内存被分配给实用程序堆。当一个数据库实用程序(例如备份、恢复、导出、导入和装载)启动时,才会按 util_heap_sz指定的大小分配全额的内存。
主缓冲池
数据库缓冲池通常是数据库共享内存中最大的一块内存。DB2
在其中操纵所有常规数据和索引数据。一个数据库必须至少有一个缓冲池,并且可以有多个缓冲池,这要视工作负载的特征、数据库中使用的数据库页面大小等因素而定。例如,页面大小为
8KB 的表空间只能使用页面大小为 8KB 的缓冲池。
可以通过
CREATE BUFFERPOOL 语句中的 EXTENDED STORAGE
选项“扩展”缓冲池。扩展的存储(ESTORE)充当的是从缓冲池中被逐出的页的辅助缓存,这样可以减少 I/O。ESTORE 的大小由
num_estore_segs 和 estore_seg_sz 这两个数据库配置参数来控制。如果使用
ESTORE,那么就要从数据库共享内存中拿出一定的内存,用于管理
ESTORE,这意味着用于其他内存池的内存将更少。
这时您可能要问,为什么要这么麻烦去使用 ESTORE?为什么不分配一个更大的缓冲池呢?答案跟可寻址内存(而不是物理内存)的限制有关,我们在后面会加以讨论。
隐藏的缓冲池
当数据库启动时,要分配 4 个页宽分别为 4K、8K、16K 和 32K 的小型缓冲池。这些缓冲池是“隐藏”的,因为在系统编目中看不到它们(通过 SELECT * FROM SYSCAT.BUFFERPOOLS 显示不出)。
如果主缓冲池配置得太大,则可能出现主缓冲池不适合可寻址内存空间的情况。(我们在后面会谈到可寻址内存。)这意味着
DB2
无法启动数据库,因为一个数据库至少必须有一个缓冲池。如果数据库没有启动,那么就不能连接到数据库,也就不能更改缓冲池的大小。由于这个原因,DB2
预先分配了 4 个这样的小型缓冲池。这样,一旦主缓冲池无法启动,DB2
还可以使用这些小型的缓冲池来启动数据库。(在此情况下,用户将收到一条警告(SQLSTATE
01626))。这时,应该连接到数据库,并减少主缓冲池的大小。
排序堆的阈值( sheapthres, sheapthres_shr)
最大应用程序内存集大小 (4KB) (APPGROUP_MEM_SZ) = 40000
最大应用程序控制堆大小 (4KB) (APP_CTL_HEAP_SZ) = 512
用于应用程序组堆的内存所占百分比 (GROUPHEAP_RATIO) = 70
可以计算出下面的值:
因此,groupheap_ratio 越高,应用程序组共享堆就越大,从而用于每个应用程序的应用程序控制堆就越小。
代理私有内存
代理的数量受下面两者中的较低者限制:
Application Heap ( applheapsz)
Sort Heap ( sortheap)
Statement Heap ( stmtheap)
Statistics Heap ( stat_heap_sz)
Query Heap ( query_heap_sz)
Java Interpreter Heap ( java_heap_sz)
Agent Stack Size ( agent_stack_sz) (仅适用于 Windows)
我们曾提到,私有内存是在一个
DB2 代理被“指派”执行任务时分配给该代理的。那么,私有内存何时释放呢?答案取决于 dbm cfg 参数
num_poolagents的值。该参数的值指定任何时候可以保留的闲置代理的最大数目。如果该值为
0,那么就不允许有限制代理。只要一个代理完成了它的工作,这个代理就要被销毁,它的内存也要返回给操作系统。如果该参数被设为一个非零值,那么一个代理在完成其工作后不会被销毁。相反,它将被返回到闲置代理池,直到闲置代理的数目到达
num_poolagents指定的最大值。当传入一个新的请求时,就要调用这些闲置代理来服务该新请求。这样就减少了创建和销毁代理的开销。
图
1展示了一个 DB2 实例的 DB2 内存结构。 图 4将展示在同一个系统上有两个实例并发运行的情况。虚拟内存包括物理 RAM
和调页空间(paging space)。共享内存“倾向于”留在 RAM
中,因为对它们的访问更频繁。如果代理闲置了较长的一段时间,则其代理私有内存将被调出。
共享内存与私有内存
为了理解共享内存与私有内存之间的不同之处,首先让我们通过快速阅读
DB2 进程 model来了解一下 DB2 代理进程。在 DB2 中,所有数据库请求都是由 DB2
代理或子代理来服务的。例如,当一个应用程序连接到一个数据库时,就有一个 DB2 代理指派给它。当该应用程序发出任何数据库请求(例如一个
SQL 查询)时,该代理就会出来执行完成这个查询所需的所有任务 —— 它代表该应用程序工作。(如果数据库是分区的,或者启用了
intra-parallel,那么可以分配不止一个的代理来代表应用程序工作。这些代理叫做 子代理。)
除了私有内存(代理在其中使用
排序堆执行“私有”任务,例如私有排序)外,代理还需要数据库级的资源,例如缓冲池、
locklist和日志缓冲区。这些资源在数据库共享内存中(参见 图 1)。 DB2
的工作方式是,数据库共享内存中的所有资源都由连接到相同数据库的所有代理或子代理共享。因此,该内存集被称作共享内存,而不是私有内存。例如,连接到数据库
A 的代理 x 使用数据库 A 的数据库共享内存中的资源。现在又有一个代理,即代理 y 也连接到数据库 A。那么代理 y 将与代理 x
共享数据库 A 的数据库内存。(当然,代理 x 和代理 y 都有其自己的代理私有内存,这些代理私有内存不是共享的。)
下图展示了当两个 DB2 代理(代理 x 和代理 y)连接到数据库 A 时分配的 DB2 内存集。假设:
数据库 A 属于实例 db2inst1。
数据库 A 为应用程序组 1 启用了 intra-parallel。
代理 x 和 代理 y 都属于应用程序组 1。
图 5 - DB2 代理进程内存地址空间
图
5 展示了在 RAM 中分配的以下内存集:
用于实例 db2inst1 的实例共享内存集。
用于 数据库 A 的数据库共享内存集。
用于 应用程序组 1 的应用程序组共享内存。
用于代理 x 的代理私有内存集。
用于代理 y 的代理私有内存集。
为内核和库之类的东西预留的内存。
代理 x 和代理 y 共享相同的实例内存、数据库内存和应用程序组内存,因为它们属于相同的实例、相同的数据库和相同的应用程序组。此外,它们有其自己的代理私有内存。
每个 DB2 代理进程都有其自己的内存地址空间。在内存空间中的内存地址允许代理访问物理 RAM 中的内存。我们可以把这些地址看作指向 RAM 的指针,如 图 5所示。对于任何 DB2 进程,这个地址空间必须能够容纳上述所有 4 种内存集。
前面已提到,ESTORE
是用于扩展缓冲池的大小。那么您可能要问,为什么不创建一个更大的缓冲池呢。答案是:因为地址空间受到限制,地址空间可能容不下一个更大的缓冲池!在此情况下,需要定义一个较小的地址空间能够容纳的缓冲池。如果有过量的物理内存,那么可以用该内存来配置
ESTORE。
那么,我们怎么知道一个 DB2 代理的地址空间是多大呢?地址空间的大小取决于当前的实例是 32 位的实例还是 64 位的实例。我们将在下一节对此进行解释。
32 位体系结构与 64 位体系结构中的可寻址内存
如果有一个
64 位的 DB2 实例,则意味着 DB2 使用的是 64 位的内存体系结构。在这种体系结构中,对于所有平台,每个进程的地址空间都是 2 的
64 次方,或者 18,446,744,073 GB。这是一个相当巨大的内存。将所有 DB2 内存集放入这个地址空间应该没有问题。
另一方面,如果有一个
32 位 DB2 实例,则对于所有平台,地址空间只有 2 的 32 次方,或者 4 GB(Linux/390
平台除外,在此平台下地址空间实际上只有 2 的 31 次方。不过,在本文中我们不讨论 Linux/390 中的 DB2)。所以,不管物理 RAM
有多大,要使一个 DB2
进程能够访问它所需的所有资源,包括实例共享内存、数据库共享内存、应用程序组共享内存、它自己的代理私有内存以及用于内核的内存等,所有这些资源必须能放入到
4GB 的地址空间内。
这就导致了两个非常重要的问题:
应该为实例内存、数据库内存和应用程序共享内存分配多少的内存,以使它们能放入到 4GB 的可寻址空间?
应该如何配置 图 1中列出的每个参数,以最有效地利用可用的内存?
虽然
4GB 的地址空间限制适用于所有平台,但是对于上述问题的回答却与平台有关。例如,在 AIX 系统上可以分配给 DB2
数据库的最大数据库内存数就与 Solaris 系统上的数据库不同。接下来的几节将讨论不同的平台对 DB2
中的内存配置有何影响。注意:本文的后续部分只针对 32 位内存体系结构。我们即将讨论的问题不适用于 64
位的体系结构。
32 位 AIX 中的 DB2 内存配置
在
32 位的 AIX 上,4GB 的可寻址内存空间被拆分为 16 个段,每段 256MB。 图 6展示了用于一个 DB2 代理进程的 32位
内存地址空间。(假设 DB2_MMAP_READ 和 DB2_MMA_WRITE 这两个 DB2 注册表变量都被设为
NO。如果这两个变量没有设为 NO,则表示方法会有点不同。我们将在后面解释。)
图 6 - AIX 中的 DB2 32 位内存地址空间
段
0 - 预留给 AIX 内核。
段 1 - 预留给 db2sysc 进程。
段 2 - 预留给代理私有内存。
段 3 - 预留给实例共享内存。
段 4 到段 B - 数据库共享内存始于段 4,这些段必须紧挨在一起。所有这 8 个段(2GB)可能都被用于数据库共享内存。但是,下面的每种配置都会从数据库共享内存中拿出一个段(256MB)。
注意: 对于下面的每种配置,DB2 将从数据库共享内存中拿出一个段,这个段始于段 B。
如果数据库是分区的,或者启用了 intra-parallel 或连接集中器,那么数据库共享内存中有一个段被预留给应用程序组共享内存。
Fast
Communication Manager (FCM):FCM 用于系统物理节点上不同分区之间的通信。默认情况下,这种通信是通过 UNIX
socket 进行的。如果 DB2_FORCE_FCM_BP 被设为 YES,那么 FCM
通信发生在共享内存内。这意味着数据库共享内存中有一个段被预留给 FCM 通信。虽然 FCM
通信变得更快,但是它也令数据库共享内存减少了一个段。
fenced UDF 和存储过程:如果数据库上运行着一个 fenced 函数或过程,那么数据库共享内存中有一个段要预留给 fenced 模式的通信。
如果数据库允许任何本地连接,那么数据库共享内存中有一个段要预留给代理/本地应用程序通信。如果将所有本地连接配置为
loopback 连接,那么就可以为这些连接使用
TCP/IP,而不需要共享内存(即使数据库就在服务器本地)。这样就有效地为数据库共享内存空出一个段来。 然而,如果您不想使用 loopback
解决方案,还有一种方法可以迫使
DB2 选择段 E 来用于代理/本地应用程序通信,这样数据库共享内存就不受影响(即不会减少)。请参阅后面的解释。
如果启用了
ESTORE,那么还要从数据库共享内存中拿出另一个段。因此,如果启用 ESTORE,则应确保它至少是
256MB,否则就不起作用,因为要从数据库共享内存中拿出一个 256 MB 的段来仅用于管理这个 ESTORE。建议将 estore 段的大小(
estore_seg_sz)设为 256MB,然后根据可用的内存更改段的数目(
num_estore_segs)。
段 C - 预留给 DB2 跟踪使用程序。
段 D 和 F- 预留给 DB2 共享库
段 E - 在默认情况下,这个段是不用的。不过,如果设置 DB2_MMAP_READ=NO 和 DB2_MMAP_WRITE=NO,那么该段用于 DB2 代理以及本地应用程序之间的通信(如 图 6所示)。这将有效地为数据库共享内存一个段。
注意: 为了最大化数据库共享内存的空间,应使用以下注册表变量设置:DB2_FORCE_FCM_BP=NO (该值是默认值),DB2_MMAP_READ=NO,DB2_MMAP_WRITE=NO。
需要从这种结构中了解到的最重要的事情是:
对于禁用了 intra-parallel 的单分区系统,我们可以得到至多 2GB 的空间用于数据库共享内存(段 4 到段 B)。
下面每种配置都将数据库共享内存减少了一个段(256MB):带
fenced UDF 或存储过程的数据库、带本地连接的数据库、DB2_FORCE_FCM_BP=YES 情况下的数据库、支持
intra_parallel、或支持集中器以及分区的数据库,以及启用了 ESTORE 的数据库。
如果允许与数据库进行本地连接,那么应该将 DB2_MMAP_READ 和 DB2_MMAP_WRITE 都设置成 NO,以便使用段 E。否则,任何本地连接都要从数据库共享内存中拿走一个段。
这些限制规定了我们该如何配置数据库共享内存集中的每个内存池。可以使用前面给出的公式来计算数据库共享内存。得到的总和不能超过这个限制。
注意:内存可以分配,释放,也可以当数据库正在运行时在不同区域之间交换。例如,您可以减少 locklist 而增加相同数量的任何一个给定的缓冲池。
使用 svmon 监控 AIX 上的内存使用情况
在
AIX 上,除了 db2mtrk 工具外,还可以使用 svmon 工具来监控 DB2 代理进程的内存消耗情况(需要 root
权限)。这个命令是: "svmon -P PID",其中 PID是 DB2 代理(db2agent 或 db2agentp)的进程 ID。
图 7 展示了 svmon 对于一个名为 db2agent 的进程的示例输出。与这个 db2agent 进程相关的数据库有如下特征: